@0xsequence/catapult 1.3.17 → 1.5.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 (232) hide show
  1. package/README.md +276 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +1 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/index.d.ts +1 -0
  6. package/dist/commands/index.d.ts.map +1 -1
  7. package/dist/commands/index.js +1 -0
  8. package/dist/commands/index.js.map +1 -1
  9. package/dist/commands/list.d.ts.map +1 -1
  10. package/dist/commands/list.js +12 -0
  11. package/dist/commands/list.js.map +1 -1
  12. package/dist/commands/provenance.d.ts +3 -0
  13. package/dist/commands/provenance.d.ts.map +1 -0
  14. package/dist/commands/provenance.js +138 -0
  15. package/dist/commands/provenance.js.map +1 -0
  16. package/dist/lib/__tests__/deployer.spec.js +118 -1
  17. package/dist/lib/__tests__/deployer.spec.js.map +1 -1
  18. package/dist/lib/__tests__/network-loader.spec.js.map +1 -1
  19. package/dist/lib/__tests__/provenance.spec.d.ts +2 -0
  20. package/dist/lib/__tests__/provenance.spec.d.ts.map +1 -0
  21. package/dist/lib/__tests__/provenance.spec.js +205 -0
  22. package/dist/lib/__tests__/provenance.spec.js.map +1 -0
  23. package/dist/lib/contracts/__tests__/repository.spec.js +243 -0
  24. package/dist/lib/contracts/__tests__/repository.spec.js.map +1 -1
  25. package/dist/lib/contracts/repository.d.ts +9 -1
  26. package/dist/lib/contracts/repository.d.ts.map +1 -1
  27. package/dist/lib/contracts/repository.js +93 -7
  28. package/dist/lib/contracts/repository.js.map +1 -1
  29. package/dist/lib/core/__tests__/assert-action.spec.d.ts +2 -0
  30. package/dist/lib/core/__tests__/assert-action.spec.d.ts.map +1 -0
  31. package/dist/lib/core/__tests__/assert-action.spec.js +377 -0
  32. package/dist/lib/core/__tests__/assert-action.spec.js.map +1 -0
  33. package/dist/lib/core/__tests__/engine.spec.js +80 -0
  34. package/dist/lib/core/__tests__/engine.spec.js.map +1 -1
  35. package/dist/lib/core/__tests__/loader.spec.js +29 -0
  36. package/dist/lib/core/__tests__/loader.spec.js.map +1 -1
  37. package/dist/lib/core/__tests__/resolver.spec.js +405 -0
  38. package/dist/lib/core/__tests__/resolver.spec.js.map +1 -1
  39. package/dist/lib/core/__tests__/sign-actions.spec.d.ts +2 -0
  40. package/dist/lib/core/__tests__/sign-actions.spec.d.ts.map +1 -0
  41. package/dist/lib/core/__tests__/sign-actions.spec.js +128 -0
  42. package/dist/lib/core/__tests__/sign-actions.spec.js.map +1 -0
  43. package/dist/lib/core/__tests__/signer.spec.d.ts +2 -0
  44. package/dist/lib/core/__tests__/signer.spec.d.ts.map +1 -0
  45. package/dist/lib/core/__tests__/signer.spec.js +40 -0
  46. package/dist/lib/core/__tests__/signer.spec.js.map +1 -0
  47. package/dist/lib/core/context.d.ts +3 -2
  48. package/dist/lib/core/context.d.ts.map +1 -1
  49. package/dist/lib/core/context.js +3 -2
  50. package/dist/lib/core/context.js.map +1 -1
  51. package/dist/lib/core/engine.d.ts +4 -0
  52. package/dist/lib/core/engine.d.ts.map +1 -1
  53. package/dist/lib/core/engine.js +206 -0
  54. package/dist/lib/core/engine.js.map +1 -1
  55. package/dist/lib/core/loader.d.ts +1 -0
  56. package/dist/lib/core/loader.d.ts.map +1 -1
  57. package/dist/lib/core/loader.js +6 -1
  58. package/dist/lib/core/loader.js.map +1 -1
  59. package/dist/lib/core/resolver.d.ts +2 -0
  60. package/dist/lib/core/resolver.d.ts.map +1 -1
  61. package/dist/lib/core/resolver.js +89 -0
  62. package/dist/lib/core/resolver.js.map +1 -1
  63. package/dist/lib/core/signer.d.ts +7 -0
  64. package/dist/lib/core/signer.d.ts.map +1 -0
  65. package/dist/lib/core/signer.js +60 -0
  66. package/dist/lib/core/signer.js.map +1 -0
  67. package/dist/lib/deployer.d.ts.map +1 -1
  68. package/dist/lib/deployer.js +21 -4
  69. package/dist/lib/deployer.js.map +1 -1
  70. package/dist/lib/index.d.ts +1 -0
  71. package/dist/lib/index.d.ts.map +1 -1
  72. package/dist/lib/index.js +1 -0
  73. package/dist/lib/index.js.map +1 -1
  74. package/dist/lib/parsers/__tests__/job.spec.js +77 -0
  75. package/dist/lib/parsers/__tests__/job.spec.js.map +1 -1
  76. package/dist/lib/parsers/__tests__/source.spec.d.ts +2 -0
  77. package/dist/lib/parsers/__tests__/source.spec.d.ts.map +1 -0
  78. package/dist/lib/parsers/__tests__/source.spec.js +158 -0
  79. package/dist/lib/parsers/__tests__/source.spec.js.map +1 -0
  80. package/dist/lib/parsers/index.d.ts +1 -0
  81. package/dist/lib/parsers/index.d.ts.map +1 -1
  82. package/dist/lib/parsers/index.js +1 -0
  83. package/dist/lib/parsers/index.js.map +1 -1
  84. package/dist/lib/parsers/job.d.ts.map +1 -1
  85. package/dist/lib/parsers/job.js +11 -0
  86. package/dist/lib/parsers/job.js.map +1 -1
  87. package/dist/lib/parsers/source.d.ts +4 -0
  88. package/dist/lib/parsers/source.d.ts.map +1 -0
  89. package/dist/lib/parsers/source.js +107 -0
  90. package/dist/lib/parsers/source.js.map +1 -0
  91. package/dist/lib/provenance.d.ts +34 -0
  92. package/dist/lib/provenance.d.ts.map +1 -0
  93. package/dist/lib/provenance.js +694 -0
  94. package/dist/lib/provenance.js.map +1 -0
  95. package/dist/lib/types/actions.d.ts +42 -2
  96. package/dist/lib/types/actions.d.ts.map +1 -1
  97. package/dist/lib/types/actions.js +4 -0
  98. package/dist/lib/types/actions.js.map +1 -1
  99. package/dist/lib/types/contracts.d.ts +3 -0
  100. package/dist/lib/types/contracts.d.ts.map +1 -1
  101. package/dist/lib/types/definitions.d.ts +1 -0
  102. package/dist/lib/types/definitions.d.ts.map +1 -1
  103. package/dist/lib/types/index.d.ts +1 -0
  104. package/dist/lib/types/index.d.ts.map +1 -1
  105. package/dist/lib/types/index.js +1 -0
  106. package/dist/lib/types/index.js.map +1 -1
  107. package/dist/lib/types/source.d.ts +26 -0
  108. package/dist/lib/types/source.d.ts.map +1 -0
  109. package/dist/lib/types/source.js +3 -0
  110. package/dist/lib/types/source.js.map +1 -0
  111. package/dist/lib/types/values.d.ts +33 -1
  112. package/dist/lib/types/values.d.ts.map +1 -1
  113. package/package.json +4 -1
  114. package/.eslintrc.json +0 -29
  115. package/.github/workflows/ci.yml +0 -181
  116. package/CONCEPT.md +0 -24
  117. package/contracts/checked-call.huff +0 -65
  118. package/eslint.config.js +0 -48
  119. package/examples/jobs/guards-v1.yaml +0 -17
  120. package/examples/jobs/sequence-seq-0001-patch.yaml +0 -59
  121. package/examples/jobs/sequence-v1.yaml +0 -59
  122. package/examples/templates/sequence-factory-v1.yaml +0 -56
  123. package/jest.config.js +0 -25
  124. package/src/cli.ts +0 -17
  125. package/src/commands/common.ts +0 -61
  126. package/src/commands/dry.ts +0 -209
  127. package/src/commands/etherscan.ts +0 -360
  128. package/src/commands/index.ts +0 -5
  129. package/src/commands/list.ts +0 -249
  130. package/src/commands/run.ts +0 -146
  131. package/src/commands/utils.ts +0 -215
  132. package/src/index.ts +0 -67
  133. package/src/lib/__tests__/deployer-events.spec.ts +0 -338
  134. package/src/lib/__tests__/deployer.spec.ts +0 -2093
  135. package/src/lib/__tests__/network-loader.spec.ts +0 -150
  136. package/src/lib/__tests__/network-selection.spec.ts +0 -41
  137. package/src/lib/__tests__/network-utils.spec.ts +0 -230
  138. package/src/lib/artifacts/__tests__/fixtures/contract1.json +0 -19
  139. package/src/lib/artifacts/__tests__/fixtures/contract2.json +0 -19
  140. package/src/lib/artifacts/__tests__/fixtures/duplicate-name.json +0 -19
  141. package/src/lib/artifacts/__tests__/fixtures/nested/nested-contract.json +0 -18
  142. package/src/lib/artifacts/__tests__/fixtures/not-an-artifact.json +0 -8
  143. package/src/lib/artifacts/__tests__/fixtures/readme.txt +0 -2
  144. package/src/lib/contracts/__tests__/repository.spec.ts +0 -344
  145. package/src/lib/contracts/repository.ts +0 -313
  146. package/src/lib/core/__tests__/context.spec.ts +0 -37
  147. package/src/lib/core/__tests__/engine.spec.ts +0 -1889
  148. package/src/lib/core/__tests__/graph.spec.ts +0 -125
  149. package/src/lib/core/__tests__/json-integration.spec.ts +0 -425
  150. package/src/lib/core/__tests__/loader.spec.ts +0 -334
  151. package/src/lib/core/__tests__/multi-platform-verification.spec.ts +0 -406
  152. package/src/lib/core/__tests__/resolver.spec.ts +0 -2053
  153. package/src/lib/core/__tests__/static-action.spec.ts +0 -172
  154. package/src/lib/core/context.ts +0 -127
  155. package/src/lib/core/engine.ts +0 -1782
  156. package/src/lib/core/graph.ts +0 -252
  157. package/src/lib/core/loader.ts +0 -247
  158. package/src/lib/core/resolver.ts +0 -757
  159. package/src/lib/deployer.ts +0 -981
  160. package/src/lib/events/__tests__/event-system.spec.ts +0 -392
  161. package/src/lib/events/cli-adapter.ts +0 -369
  162. package/src/lib/events/emitter.ts +0 -62
  163. package/src/lib/events/index.ts +0 -3
  164. package/src/lib/events/types.ts +0 -520
  165. package/src/lib/index.ts +0 -14
  166. package/src/lib/network-loader.ts +0 -90
  167. package/src/lib/network-selection.ts +0 -73
  168. package/src/lib/network-utils.ts +0 -64
  169. package/src/lib/parsers/__tests__/buildinfo.spec.ts +0 -122
  170. package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-bytecode-buildinfo.json +0 -62
  171. package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-json.txt +0 -2
  172. package/src/lib/parsers/__tests__/fixtures/buildinfo/multi-contract-buildinfo.json +0 -89
  173. package/src/lib/parsers/__tests__/fixtures/buildinfo/no-contracts-buildinfo.json +0 -17
  174. package/src/lib/parsers/__tests__/fixtures/buildinfo/simple-buildinfo.json +0 -63
  175. package/src/lib/parsers/__tests__/fixtures/buildinfo/wrong-format.json +0 -4
  176. package/src/lib/parsers/__tests__/job.spec.ts +0 -358
  177. package/src/lib/parsers/__tests__/template.spec.ts +0 -111
  178. package/src/lib/parsers/artifact/__tests__/artifact.spec.ts +0 -117
  179. package/src/lib/parsers/artifact/__tests__/fixtures/empty-bytecode.json +0 -5
  180. package/src/lib/parsers/artifact/__tests__/fixtures/hardhat-artifact.json +0 -67
  181. package/src/lib/parsers/artifact/__tests__/fixtures/invalid-bytecode.json +0 -5
  182. package/src/lib/parsers/artifact/__tests__/fixtures/invalid-json.txt +0 -11
  183. package/src/lib/parsers/artifact/__tests__/fixtures/minimal-artifact.json +0 -5
  184. package/src/lib/parsers/artifact/__tests__/fixtures/missing-abi.json +0 -4
  185. package/src/lib/parsers/artifact/__tests__/fixtures/missing-bytecode.json +0 -11
  186. package/src/lib/parsers/artifact/__tests__/fixtures/missing-contract-name.json +0 -11
  187. package/src/lib/parsers/artifact/__tests__/fixtures/simple-artifact.json +0 -40
  188. package/src/lib/parsers/artifact/__tests__/fixtures/wrong-types.json +0 -7
  189. package/src/lib/parsers/artifact/foundry-1.2.ts +0 -72
  190. package/src/lib/parsers/artifact/index.ts +0 -27
  191. package/src/lib/parsers/artifact/types.ts +0 -9
  192. package/src/lib/parsers/buildinfo.ts +0 -127
  193. package/src/lib/parsers/constants.ts +0 -56
  194. package/src/lib/parsers/index.ts +0 -5
  195. package/src/lib/parsers/job.ts +0 -148
  196. package/src/lib/parsers/template.ts +0 -135
  197. package/src/lib/std/templates/arachnid-deterministic-deployment-proxy.yaml +0 -68
  198. package/src/lib/std/templates/assured-deployment.yaml +0 -46
  199. package/src/lib/std/templates/era-evm-predeploy.yaml +0 -35
  200. package/src/lib/std/templates/erc-2470.yaml +0 -70
  201. package/src/lib/std/templates/min-balance.yaml +0 -35
  202. package/src/lib/std/templates/nano-universal-deployer.yaml +0 -61
  203. package/src/lib/std/templates/raw-erc-2470.yaml +0 -62
  204. package/src/lib/std/templates/raw-nano-universal-deployer.yaml +0 -54
  205. package/src/lib/std/templates/raw-sequence-universal-deployer-2.yaml +0 -52
  206. package/src/lib/std/templates/sequence-universal-deployer-2.yaml +0 -61
  207. package/src/lib/types/__tests__/json-request-action.spec.ts +0 -243
  208. package/src/lib/types/__tests__/read-json-value.spec.ts +0 -278
  209. package/src/lib/types/__tests__/resolve-json-value.spec.ts +0 -769
  210. package/src/lib/types/actions.ts +0 -127
  211. package/src/lib/types/artifacts.ts +0 -21
  212. package/src/lib/types/buildinfo.ts +0 -116
  213. package/src/lib/types/conditions.ts +0 -50
  214. package/src/lib/types/contracts.ts +0 -23
  215. package/src/lib/types/definitions.ts +0 -70
  216. package/src/lib/types/index.ts +0 -8
  217. package/src/lib/types/network.ts +0 -33
  218. package/src/lib/types/project.ts +0 -9
  219. package/src/lib/types/task.ts +0 -9
  220. package/src/lib/types/values.ts +0 -150
  221. package/src/lib/utils/assertion.ts +0 -24
  222. package/src/lib/utils/validation.ts +0 -116
  223. package/src/lib/validation/contract-references.ts +0 -210
  224. package/src/lib/validation/index.ts +0 -1
  225. package/src/lib/verification/__tests__/etherscan.spec.ts +0 -710
  226. package/src/lib/verification/__tests__/sourcify.spec.ts +0 -288
  227. package/src/lib/verification/etherscan.ts +0 -547
  228. package/src/lib/verification/sourcify.ts +0 -248
  229. package/test_validation/artifacts/TestContract.json +0 -9
  230. package/test_validation/jobs/test-missing.yaml +0 -16
  231. package/test_validation/networks.yaml +0 -3
  232. package/tsconfig.json +0 -36
@@ -1,252 +0,0 @@
1
- // src/lib/core/graph.ts
2
- import { Job, Template, isJobCompletedCondition } from '../types'
3
- import { isPrimitiveActionType } from '../types/actions'
4
-
5
- /**
6
- * Represents the complete dependency graph of all jobs in a project.
7
- * It is responsible for parsing job and template dependencies, detecting
8
- * cycles, and providing a valid execution order.
9
- */
10
- export class DependencyGraph {
11
- private graph: Map<string, Set<string>> = new Map()
12
- private executionOrder: string[] = []
13
- private allJobNames: Set<string>
14
-
15
- constructor(
16
- private readonly jobs: Map<string, Job>,
17
- private readonly templates: Map<string, Template>,
18
- ) {
19
- this.allJobNames = new Set(this.jobs.keys())
20
- this.build()
21
- this.checkForCycles()
22
- this.executionOrder = this.topologicalSort()
23
- }
24
-
25
- /**
26
- * Returns the list of job names in a valid execution order.
27
- * Jobs with no dependencies come first.
28
- */
29
- public getExecutionOrder(): string[] {
30
- return this.executionOrder
31
- }
32
-
33
- /**
34
- * Returns the direct and transitive dependencies for a given job.
35
- */
36
- public getDependencies(jobName: string): Set<string> {
37
- return this.graph.get(jobName) || new Set()
38
- }
39
-
40
- /**
41
- * Populates the dependency graph by analyzing each job.
42
- */
43
- private build(): void {
44
- for (const jobName of this.allJobNames) {
45
- this.graph.set(jobName, this.findAllDependencies(jobName))
46
- }
47
- }
48
-
49
- /**
50
- * Recursively finds all dependencies for a given job, including those
51
- * from its `depends_on` list and those hidden within its templates' setup blocks.
52
- *
53
- * @param jobName The name of the job to analyze.
54
- * @param visited A set to track visited jobs in the current path to detect cycles.
55
- * @returns A set of all job names that the given job depends on.
56
- */
57
- private findAllDependencies(jobName: string, visited: Set<string> = new Set()): Set<string> {
58
- if (visited.has(jobName)) {
59
- // This path is cyclic. The cycle will be properly reported by `checkForCycles`.
60
- // Here, we just stop the recursion to prevent an infinite loop.
61
- return new Set()
62
- }
63
- visited.add(jobName)
64
-
65
- const job = this.jobs.get(jobName)
66
- if (!job) {
67
- throw new Error(`Integrity error: Job "${jobName}" not found during graph build.`)
68
- }
69
-
70
- const directDependencies = new Set<string>()
71
-
72
- // 1. Add dependencies from the job's `depends_on` field.
73
- job.depends_on?.forEach(dep => directDependencies.add(dep))
74
-
75
- // 2. Find dependencies within the templates used by the job's actions.
76
- for (const action of job.actions) {
77
- // Get the template or type name
78
- const templateName = action.template || action.type
79
- if (!templateName) {
80
- throw new Error(`Invalid configuration: Action in job "${jobName}" has no template or type field.`)
81
- }
82
-
83
- // If the action is a primitive, it cannot have job dependencies. Skip it.
84
- if (isPrimitiveActionType(templateName)) {
85
- continue
86
- }
87
-
88
- const template = this.templates.get(templateName)
89
- if (!template) {
90
- throw new Error(`Invalid configuration: Template "${templateName}" used by job "${jobName}" not found.`)
91
- }
92
-
93
- // Recursively find dependencies in the template's setup block.
94
- this.findTemplateSetupDependencies(template).forEach(dep => directDependencies.add(dep))
95
- }
96
-
97
- // 3. Now, for each direct dependency, find its transitive dependencies.
98
- const allDependencies = new Set<string>(directDependencies)
99
- for (const dep of directDependencies) {
100
- if (!this.allJobNames.has(dep)) {
101
- throw new Error(`Invalid dependency: Job "${jobName}" depends on "${dep}", which does not exist.`)
102
- }
103
- // The `visited` set is passed down to detect cycles across calls.
104
- const transitiveDeps = this.findAllDependencies(dep, new Set(visited))
105
- transitiveDeps.forEach(transDep => allDependencies.add(transDep))
106
- }
107
-
108
- return allDependencies
109
- }
110
-
111
- /**
112
- * Helper to extract job dependencies from a template's setup block.
113
- */
114
- private findTemplateSetupDependencies(template: Template): Set<string> {
115
- const dependencies = new Set<string>()
116
- const setup = template.setup
117
- if (!setup) return dependencies
118
-
119
- // Case 1: Dependencies from `skip_condition` (e.g., `job-completed`).
120
- setup.skip_condition?.forEach(condition => {
121
- if (isJobCompletedCondition(condition)) {
122
- dependencies.add(condition.arguments.job)
123
- }
124
- })
125
-
126
- // Case 2: Dependencies from setup actions that are themselves templates.
127
- setup.actions?.forEach(action => {
128
- // Ignore primitive actions within a setup block.
129
- if (isPrimitiveActionType(action.type)) {
130
- return
131
- }
132
-
133
- // In your YAML, setup blocks sometimes call other templates directly.
134
- // e.g. `sequence-universal-deployer-2`'s setup calls `nano-universal-deployer`
135
- // Here, `action.type` IS the template name.
136
- const actionTemplate = this.templates.get(action.type)
137
- if (actionTemplate) {
138
- // This is a nested template call, so we must find its dependencies too.
139
- this.findTemplateSetupDependencies(actionTemplate).forEach(dep => dependencies.add(dep))
140
- }
141
- })
142
-
143
- return dependencies
144
- }
145
-
146
- /**
147
- * Checks the entire graph for circular dependencies.
148
- * @throws {Error} if a cycle is detected.
149
- */
150
- private checkForCycles(): void {
151
- for (const [jobName, dependencies] of this.graph.entries()) {
152
- if (dependencies.has(jobName)) {
153
- // To provide a more helpful error, we need to find the actual path.
154
- const path = this.findPath(jobName, jobName)
155
- throw new Error(`Circular dependency detected: ${path.join(' -> ')}`)
156
- }
157
- }
158
- }
159
-
160
- /**
161
- * Helper to find a dependency path from a start node to an end node.
162
- * Used for creating helpful error messages for cycles.
163
- */
164
- private findPath(start: string, end: string, visited: Set<string> = new Set()): string[] {
165
- visited.add(start)
166
- const job = this.jobs.get(start)
167
- if (!job) return []
168
-
169
- const directDependencies = new Set(job.depends_on || [])
170
- for (const action of job.actions) {
171
- const templateName = action.template || action.type
172
- if (!templateName || isPrimitiveActionType(templateName)) {
173
- continue
174
- }
175
- const template = this.templates.get(templateName)!
176
- this.findTemplateSetupDependencies(template).forEach(dep => directDependencies.add(dep))
177
- }
178
-
179
- if (directDependencies.has(end)) {
180
- return [start, end]
181
- }
182
-
183
- for (const dep of directDependencies) {
184
- if (!visited.has(dep)) {
185
- const path = this.findPath(dep, end, visited)
186
- if (path.length > 0) {
187
- return [start, ...path]
188
- }
189
- }
190
- }
191
-
192
- return []
193
- }
194
-
195
- /**
196
- * Performs a topological sort on the graph to determine execution order.
197
- * Uses Kahn's algorithm.
198
- */
199
- private topologicalSort(): string[] {
200
- const inDegree = new Map<string, number>()
201
- const sorted: string[] = []
202
-
203
- // This map stores which jobs depend on a given key.
204
- // e.g., `adjacency.get('A')` -> `['B', 'C']` means B and C depend on A.
205
- const adjacency = new Map<string, string[]>()
206
-
207
- // Initialize in-degrees and adjacency list for all jobs.
208
- for (const jobName of this.allJobNames) {
209
- inDegree.set(jobName, 0)
210
- adjacency.set(jobName, [])
211
- }
212
-
213
- // Build the inverted graph (adjacency list) and calculate initial in-degrees.
214
- for (const [jobName, dependencies] of this.graph.entries()) {
215
- inDegree.set(jobName, dependencies.size)
216
- for (const dep of dependencies) {
217
- // `jobName` depends on `dep`, so `dep` is a prerequisite for `jobName`.
218
- adjacency.get(dep)?.push(jobName)
219
- }
220
- }
221
-
222
- // Initialize queue with nodes having an in-degree of 0.
223
- const queue: string[] = []
224
- for (const [jobName, degree] of inDegree.entries()) {
225
- if (degree === 0) {
226
- queue.push(jobName)
227
- }
228
- }
229
-
230
- // Process the queue.
231
- while (queue.length > 0) {
232
- const current = queue.shift()!
233
- sorted.push(current)
234
-
235
- const dependents = adjacency.get(current) || []
236
- for (const dependent of dependents) {
237
- const newDegree = (inDegree.get(dependent) || 1) - 1
238
- inDegree.set(dependent, newDegree)
239
- if (newDegree === 0) {
240
- queue.push(dependent)
241
- }
242
- }
243
- }
244
-
245
- if (sorted.length !== this.allJobNames.size) {
246
- // This should theoretically be caught by `checkForCycles`, but it's good defense.
247
- throw new Error('Topological sort failed. The graph likely has a cycle.')
248
- }
249
-
250
- return sorted
251
- }
252
- }
@@ -1,247 +0,0 @@
1
- import * as fs from 'fs/promises'
2
- import * as path from 'path'
3
- import { parseJob, parseTemplate } from '../parsers'
4
- import { Job, Template } from '../types'
5
- import { ContractRepository } from '../contracts/repository'
6
- import { parseConstants } from '../parsers/constants'
7
-
8
- export interface ProjectLoaderOptions {
9
- loadStdTemplates?: boolean
10
- }
11
-
12
- export class ProjectLoader {
13
- public jobs: Map<string, Job> = new Map()
14
- public templates: Map<string, Template> = new Map()
15
- public readonly contractRepository: ContractRepository
16
-
17
- // Top-level constants registry
18
- public constants: Map<string, any> = new Map()
19
- // Track source files for constants for duplicate reporting
20
- private constantSources: Map<string, string> = new Map()
21
-
22
- constructor(
23
- private readonly projectRoot: string,
24
- private readonly options: ProjectLoaderOptions = {}
25
- ) {
26
- this.contractRepository = new ContractRepository()
27
- }
28
-
29
- async load() {
30
- // Load all contracts from the project root first
31
- await this.contractRepository.loadFrom(this.projectRoot)
32
-
33
- // Load standard library templates (unless disabled)
34
- if (this.options.loadStdTemplates !== false) {
35
- const stdTemplatePath = path.resolve(__dirname, '..', 'std', 'templates')
36
- if (await this.pathExists(stdTemplatePath)) {
37
- await this.loadTemplatesFromDir(stdTemplatePath)
38
- }
39
- }
40
-
41
- // Load user-defined templates
42
- const userTemplatePath = path.join(this.projectRoot, 'templates')
43
- if (await this.pathExists(userTemplatePath)) {
44
- await this.loadTemplatesFromDir(userTemplatePath)
45
- }
46
-
47
- // Load jobs
48
- const jobsPath = path.join(this.projectRoot, 'jobs')
49
- if (await this.pathExists(jobsPath)) {
50
- await this.loadJobsFromDir(jobsPath)
51
- }
52
-
53
- // Load templates from within job directories
54
- if (await this.pathExists(jobsPath)) {
55
- await this.loadTemplatesFromJobDirs(jobsPath)
56
- }
57
-
58
- // Load top-level constants from anywhere in project root
59
- await this.loadConstantsFromDir(this.projectRoot)
60
- }
61
-
62
- private async loadTemplatesFromDir(dir: string) {
63
- const templateFiles = await this.findTemplateFiles(dir)
64
- for (const filePath of templateFiles) {
65
- try {
66
- const content = await fs.readFile(filePath, 'utf-8')
67
- const template = parseTemplate(content)
68
- template._path = filePath
69
- this.templates.set(template.name, template)
70
- } catch (error) {
71
- // Surface YAML/template parsing errors so malformed templates fail the load
72
- if (error instanceof Error && (error.message.startsWith('Failed to parse template YAML:') || error.message.startsWith('Invalid template'))) {
73
- throw new Error(`Template load error in ${filePath}: ${error.message}`)
74
- }
75
- // If it's a file system error (e.g., permission), rethrow to make it visible as well
76
- if (error instanceof Error && (error as any).code) {
77
- throw new Error(`Failed to read template file ${filePath}: ${(error as any).code} ${error.message}`)
78
- }
79
- // Otherwise rethrow to avoid silently ignoring real issues
80
- throw error
81
- }
82
- }
83
- }
84
-
85
- /**
86
- * Recursively finds all template files (.yaml/.yml) in a directory.
87
- */
88
- private async findTemplateFiles(dir: string, ignoreDirs: Set<string> = new Set(['node_modules', 'dist', '.git', '.idea', '.vscode'])): Promise<string[]> {
89
- let results: string[] = []
90
- try {
91
- const list = await fs.readdir(dir, { withFileTypes: true })
92
-
93
- for (const dirent of list) {
94
- const fullPath = path.resolve(dir, dirent.name)
95
- if (dirent.isDirectory()) {
96
- if (!ignoreDirs.has(dirent.name)) {
97
- results = results.concat(await this.findTemplateFiles(fullPath, ignoreDirs))
98
- }
99
- } else if (dirent.isFile() && (dirent.name.endsWith('.yaml') || dirent.name.endsWith('.yml'))) {
100
- results.push(fullPath)
101
- }
102
- }
103
- } catch (err) {
104
- // Ignore errors from trying to read directories we don't have access to, etc.
105
- }
106
- return results
107
- }
108
-
109
- private async loadJobsFromDir(dir: string) {
110
- const jobFiles = await this.findJobFiles(dir)
111
- for (const filePath of jobFiles) {
112
- try {
113
- const content = await fs.readFile(filePath, 'utf-8')
114
- const raw: any = (() => { try { return require('yaml').parse(content) } catch { return {} } })()
115
- if (raw && typeof raw === 'object' && raw.type === 'template') {
116
- // This is actually a template file; load through the template path to avoid misclassification
117
- const template = parseTemplate(content)
118
- template._path = filePath
119
- this.templates.set(template.name, template)
120
- continue
121
- }
122
-
123
- const job = parseJob(content)
124
- job._path = filePath
125
- this.jobs.set(job.name, job)
126
- } catch (error) {
127
- // If job YAML is malformed or invalid, skip this job but continue loading others
128
- if (error instanceof Error && (error.message.startsWith('Failed to parse job YAML:') || error.message.startsWith('Invalid job'))) {
129
- console.warn(`Skipping malformed job at ${filePath}: ${error.message}`)
130
- continue
131
- }
132
- // If it's a file system error (e.g., permission), rethrow to make it visible as well
133
- if (error instanceof Error && (error as any).code) {
134
- throw new Error(`Failed to read job file ${filePath}: ${(error as any).code} ${error.message}`)
135
- }
136
- // For other unexpected errors, rethrow
137
- throw error
138
- }
139
- }
140
- }
141
-
142
- /**
143
- * Recursively finds all job files (.yaml/.yml) in a directory.
144
- */
145
- private async findJobFiles(dir: string, ignoreDirs: Set<string> = new Set(['node_modules', 'dist', '.git', '.idea', '.vscode'])): Promise<string[]> {
146
- let results: string[] = []
147
- try {
148
- const list = await fs.readdir(dir, { withFileTypes: true })
149
-
150
- for (const dirent of list) {
151
- const fullPath = path.resolve(dir, dirent.name)
152
- if (dirent.isDirectory()) {
153
- if (!ignoreDirs.has(dirent.name)) {
154
- results = results.concat(await this.findJobFiles(fullPath, ignoreDirs))
155
- }
156
- } else if (dirent.isFile() && (dirent.name.endsWith('.yaml') || dirent.name.endsWith('.yml'))) {
157
- results.push(fullPath)
158
- }
159
- }
160
- } catch (err) {
161
- // Ignore errors from trying to read directories we don't have access to, etc.
162
- }
163
- return results
164
- }
165
-
166
- /**
167
- * Loads templates from within job directories by scanning for 'templates' subdirectories.
168
- */
169
- private async loadTemplatesFromJobDirs(jobsRootDir: string) {
170
- await this.findAndLoadTemplatesInJobDirs(jobsRootDir)
171
- }
172
-
173
- /**
174
- * Recursively searches for 'templates' directories within job directories and loads templates from them.
175
- */
176
- private async findAndLoadTemplatesInJobDirs(dir: string, ignoreDirs: Set<string> = new Set(['node_modules', 'dist', '.git', '.idea', '.vscode'])): Promise<void> {
177
- try {
178
- const list = await fs.readdir(dir, { withFileTypes: true })
179
-
180
- for (const dirent of list) {
181
- const fullPath = path.resolve(dir, dirent.name)
182
- if (dirent.isDirectory()) {
183
- if (!ignoreDirs.has(dirent.name)) {
184
- // If this directory is named 'templates', load templates from it
185
- if (dirent.name === 'templates') {
186
- await this.loadTemplatesFromDir(fullPath)
187
- }
188
- // Continue recursively searching for more template directories
189
- await this.findAndLoadTemplatesInJobDirs(fullPath, ignoreDirs)
190
- }
191
- }
192
- }
193
- } catch (err) {
194
- // Ignore errors from trying to read directories we don't have access to, etc.
195
- }
196
- }
197
-
198
- /**
199
- * Load and merge all top-level constants from any YAML file with type: "constants"
200
- * located anywhere under the given directory.
201
- */
202
- private async loadConstantsFromDir(dir: string, ignoreDirs: Set<string> = new Set(['node_modules', 'dist', '.git', '.idea', '.vscode'])): Promise<void> {
203
- try {
204
- const list = await fs.readdir(dir, { withFileTypes: true })
205
- for (const dirent of list) {
206
- const fullPath = path.resolve(dir, dirent.name)
207
- if (dirent.isDirectory()) {
208
- if (!ignoreDirs.has(dirent.name)) {
209
- await this.loadConstantsFromDir(fullPath, ignoreDirs)
210
- }
211
- } else if (dirent.isFile() && (dirent.name.endsWith('.yaml') || dirent.name.endsWith('.yml'))) {
212
- try {
213
- const content = await fs.readFile(fullPath, 'utf-8')
214
- const constantsDoc = parseConstants(content)
215
- if (constantsDoc) {
216
- for (const [key, value] of Object.entries(constantsDoc.constants)) {
217
- if (this.constants.has(key)) {
218
- const prevSource = this.constantSources.get(key)
219
- throw new Error(`Duplicate constant "${key}" found in ${fullPath}${prevSource ? ` (previously defined in ${prevSource})` : ''}`)
220
- }
221
- this.constants.set(key, value)
222
- this.constantSources.set(key, fullPath)
223
- }
224
- }
225
- } catch (err) {
226
- // For constants files, surface parsing errors to fail fast
227
- if (err instanceof Error && (err.message.startsWith('Failed to parse constants YAML:') || err.message.startsWith('Invalid constants'))) {
228
- throw new Error(`Constants load error in ${fullPath}: ${err.message}`)
229
- }
230
- // Otherwise, ignore if not a constants file
231
- }
232
- }
233
- }
234
- } catch (err) {
235
- // Ignore directory read errors (like permission or non-existent)
236
- }
237
- }
238
-
239
- private async pathExists(p: string): Promise<boolean> {
240
- try {
241
- await fs.access(p)
242
- return true
243
- } catch {
244
- return false
245
- }
246
- }
247
- }