@0xsequence/catapult 1.4.0 → 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 (163) hide show
  1. package/README.md +27 -0
  2. package/dist/lib/__tests__/network-loader.spec.js.map +1 -1
  3. package/dist/lib/core/__tests__/resolver.spec.js +22 -0
  4. package/dist/lib/core/__tests__/resolver.spec.js.map +1 -1
  5. package/dist/lib/core/__tests__/sign-actions.spec.d.ts +2 -0
  6. package/dist/lib/core/__tests__/sign-actions.spec.d.ts.map +1 -0
  7. package/dist/lib/core/__tests__/sign-actions.spec.js +128 -0
  8. package/dist/lib/core/__tests__/sign-actions.spec.js.map +1 -0
  9. package/dist/lib/core/__tests__/signer.spec.d.ts +2 -0
  10. package/dist/lib/core/__tests__/signer.spec.d.ts.map +1 -0
  11. package/dist/lib/core/__tests__/signer.spec.js +40 -0
  12. package/dist/lib/core/__tests__/signer.spec.js.map +1 -0
  13. package/dist/lib/core/context.d.ts +3 -2
  14. package/dist/lib/core/context.d.ts.map +1 -1
  15. package/dist/lib/core/context.js +3 -2
  16. package/dist/lib/core/context.js.map +1 -1
  17. package/dist/lib/core/engine.d.ts +4 -0
  18. package/dist/lib/core/engine.d.ts.map +1 -1
  19. package/dist/lib/core/engine.js +173 -0
  20. package/dist/lib/core/engine.js.map +1 -1
  21. package/dist/lib/core/signer.d.ts +7 -0
  22. package/dist/lib/core/signer.d.ts.map +1 -0
  23. package/dist/lib/core/signer.js +60 -0
  24. package/dist/lib/core/signer.js.map +1 -0
  25. package/dist/lib/parsers/__tests__/source.spec.js +37 -0
  26. package/dist/lib/parsers/__tests__/source.spec.js.map +1 -1
  27. package/dist/lib/parsers/source.js +1 -1
  28. package/dist/lib/parsers/source.js.map +1 -1
  29. package/dist/lib/provenance.js +51 -2
  30. package/dist/lib/provenance.js.map +1 -1
  31. package/dist/lib/types/actions.d.ts +26 -2
  32. package/dist/lib/types/actions.d.ts.map +1 -1
  33. package/dist/lib/types/actions.js +3 -0
  34. package/dist/lib/types/actions.js.map +1 -1
  35. package/dist/lib/types/source.d.ts +2 -0
  36. package/dist/lib/types/source.d.ts.map +1 -1
  37. package/package.json +4 -1
  38. package/.eslintrc.json +0 -29
  39. package/.github/workflows/ci.yml +0 -181
  40. package/CONCEPT.md +0 -24
  41. package/contracts/checked-call.huff +0 -65
  42. package/eslint.config.js +0 -48
  43. package/examples/jobs/guards-v1.yaml +0 -17
  44. package/examples/jobs/sequence-seq-0001-patch.yaml +0 -59
  45. package/examples/jobs/sequence-v1.yaml +0 -59
  46. package/examples/templates/sequence-factory-v1.yaml +0 -56
  47. package/jest.config.js +0 -25
  48. package/src/cli.ts +0 -18
  49. package/src/commands/common.ts +0 -61
  50. package/src/commands/dry.ts +0 -209
  51. package/src/commands/etherscan.ts +0 -360
  52. package/src/commands/index.ts +0 -6
  53. package/src/commands/list.ts +0 -262
  54. package/src/commands/provenance.ts +0 -120
  55. package/src/commands/run.ts +0 -146
  56. package/src/commands/utils.ts +0 -215
  57. package/src/index.ts +0 -67
  58. package/src/lib/__tests__/deployer-events.spec.ts +0 -338
  59. package/src/lib/__tests__/deployer.spec.ts +0 -2269
  60. package/src/lib/__tests__/network-loader.spec.ts +0 -150
  61. package/src/lib/__tests__/network-selection.spec.ts +0 -41
  62. package/src/lib/__tests__/network-utils.spec.ts +0 -230
  63. package/src/lib/__tests__/provenance.spec.ts +0 -208
  64. package/src/lib/artifacts/__tests__/fixtures/contract1.json +0 -19
  65. package/src/lib/artifacts/__tests__/fixtures/contract2.json +0 -19
  66. package/src/lib/artifacts/__tests__/fixtures/duplicate-name.json +0 -19
  67. package/src/lib/artifacts/__tests__/fixtures/nested/nested-contract.json +0 -18
  68. package/src/lib/artifacts/__tests__/fixtures/not-an-artifact.json +0 -8
  69. package/src/lib/artifacts/__tests__/fixtures/readme.txt +0 -2
  70. package/src/lib/contracts/__tests__/repository.spec.ts +0 -612
  71. package/src/lib/contracts/repository.ts +0 -411
  72. package/src/lib/core/__tests__/assert-action.spec.ts +0 -474
  73. package/src/lib/core/__tests__/context.spec.ts +0 -37
  74. package/src/lib/core/__tests__/engine.spec.ts +0 -2005
  75. package/src/lib/core/__tests__/graph.spec.ts +0 -125
  76. package/src/lib/core/__tests__/json-integration.spec.ts +0 -425
  77. package/src/lib/core/__tests__/loader.spec.ts +0 -367
  78. package/src/lib/core/__tests__/multi-platform-verification.spec.ts +0 -406
  79. package/src/lib/core/__tests__/resolver.spec.ts +0 -2496
  80. package/src/lib/core/__tests__/static-action.spec.ts +0 -172
  81. package/src/lib/core/context.ts +0 -127
  82. package/src/lib/core/engine.ts +0 -1834
  83. package/src/lib/core/graph.ts +0 -252
  84. package/src/lib/core/loader.ts +0 -253
  85. package/src/lib/core/resolver.ts +0 -873
  86. package/src/lib/deployer.ts +0 -1005
  87. package/src/lib/events/__tests__/event-system.spec.ts +0 -392
  88. package/src/lib/events/cli-adapter.ts +0 -369
  89. package/src/lib/events/emitter.ts +0 -62
  90. package/src/lib/events/index.ts +0 -3
  91. package/src/lib/events/types.ts +0 -520
  92. package/src/lib/index.ts +0 -17
  93. package/src/lib/network-loader.ts +0 -90
  94. package/src/lib/network-selection.ts +0 -73
  95. package/src/lib/network-utils.ts +0 -64
  96. package/src/lib/parsers/__tests__/buildinfo.spec.ts +0 -122
  97. package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-bytecode-buildinfo.json +0 -62
  98. package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-json.txt +0 -2
  99. package/src/lib/parsers/__tests__/fixtures/buildinfo/multi-contract-buildinfo.json +0 -89
  100. package/src/lib/parsers/__tests__/fixtures/buildinfo/no-contracts-buildinfo.json +0 -17
  101. package/src/lib/parsers/__tests__/fixtures/buildinfo/simple-buildinfo.json +0 -63
  102. package/src/lib/parsers/__tests__/fixtures/buildinfo/wrong-format.json +0 -4
  103. package/src/lib/parsers/__tests__/job.spec.ts +0 -439
  104. package/src/lib/parsers/__tests__/source.spec.ts +0 -134
  105. package/src/lib/parsers/__tests__/template.spec.ts +0 -111
  106. package/src/lib/parsers/artifact/__tests__/artifact.spec.ts +0 -117
  107. package/src/lib/parsers/artifact/__tests__/fixtures/empty-bytecode.json +0 -5
  108. package/src/lib/parsers/artifact/__tests__/fixtures/hardhat-artifact.json +0 -67
  109. package/src/lib/parsers/artifact/__tests__/fixtures/invalid-bytecode.json +0 -5
  110. package/src/lib/parsers/artifact/__tests__/fixtures/invalid-json.txt +0 -11
  111. package/src/lib/parsers/artifact/__tests__/fixtures/minimal-artifact.json +0 -5
  112. package/src/lib/parsers/artifact/__tests__/fixtures/missing-abi.json +0 -4
  113. package/src/lib/parsers/artifact/__tests__/fixtures/missing-bytecode.json +0 -11
  114. package/src/lib/parsers/artifact/__tests__/fixtures/missing-contract-name.json +0 -11
  115. package/src/lib/parsers/artifact/__tests__/fixtures/simple-artifact.json +0 -40
  116. package/src/lib/parsers/artifact/__tests__/fixtures/wrong-types.json +0 -7
  117. package/src/lib/parsers/artifact/foundry-1.2.ts +0 -72
  118. package/src/lib/parsers/artifact/index.ts +0 -27
  119. package/src/lib/parsers/artifact/types.ts +0 -9
  120. package/src/lib/parsers/buildinfo.ts +0 -127
  121. package/src/lib/parsers/constants.ts +0 -56
  122. package/src/lib/parsers/index.ts +0 -6
  123. package/src/lib/parsers/job.ts +0 -160
  124. package/src/lib/parsers/source.ts +0 -129
  125. package/src/lib/parsers/template.ts +0 -135
  126. package/src/lib/provenance.ts +0 -785
  127. package/src/lib/std/templates/arachnid-deterministic-deployment-proxy.yaml +0 -68
  128. package/src/lib/std/templates/assured-deployment.yaml +0 -46
  129. package/src/lib/std/templates/era-evm-predeploy.yaml +0 -35
  130. package/src/lib/std/templates/erc-2470.yaml +0 -70
  131. package/src/lib/std/templates/min-balance.yaml +0 -35
  132. package/src/lib/std/templates/nano-universal-deployer.yaml +0 -61
  133. package/src/lib/std/templates/raw-erc-2470.yaml +0 -62
  134. package/src/lib/std/templates/raw-nano-universal-deployer.yaml +0 -54
  135. package/src/lib/std/templates/raw-sequence-universal-deployer-2.yaml +0 -52
  136. package/src/lib/std/templates/sequence-universal-deployer-2.yaml +0 -61
  137. package/src/lib/types/__tests__/json-request-action.spec.ts +0 -243
  138. package/src/lib/types/__tests__/read-json-value.spec.ts +0 -278
  139. package/src/lib/types/__tests__/resolve-json-value.spec.ts +0 -769
  140. package/src/lib/types/actions.ts +0 -148
  141. package/src/lib/types/artifacts.ts +0 -21
  142. package/src/lib/types/buildinfo.ts +0 -116
  143. package/src/lib/types/conditions.ts +0 -50
  144. package/src/lib/types/contracts.ts +0 -26
  145. package/src/lib/types/definitions.ts +0 -77
  146. package/src/lib/types/index.ts +0 -9
  147. package/src/lib/types/network.ts +0 -33
  148. package/src/lib/types/project.ts +0 -9
  149. package/src/lib/types/source.ts +0 -26
  150. package/src/lib/types/task.ts +0 -9
  151. package/src/lib/types/values.ts +0 -221
  152. package/src/lib/utils/assertion.ts +0 -24
  153. package/src/lib/utils/validation.ts +0 -116
  154. package/src/lib/validation/contract-references.ts +0 -210
  155. package/src/lib/validation/index.ts +0 -1
  156. package/src/lib/verification/__tests__/etherscan.spec.ts +0 -710
  157. package/src/lib/verification/__tests__/sourcify.spec.ts +0 -288
  158. package/src/lib/verification/etherscan.ts +0 -547
  159. package/src/lib/verification/sourcify.ts +0 -248
  160. package/test_validation/artifacts/TestContract.json +0 -9
  161. package/test_validation/jobs/test-missing.yaml +0 -16
  162. package/test_validation/networks.yaml +0 -3
  163. package/tsconfig.json +0 -36
@@ -1,160 +0,0 @@
1
- import { parse as parseYaml, YAMLParseError } from 'yaml'
2
- import { Condition, Job, JobAction } from '../types'
3
-
4
- /**
5
- * Determines whether a raw YAML item is a valid Condition.
6
- * Mirrors the validation logic used for templates so job-level
7
- * skip conditions behave consistently.
8
- */
9
- function isCondition(item: any): item is Condition {
10
- if (!item || typeof item !== 'object' || typeof item.type !== 'string') {
11
- return false
12
- }
13
-
14
- if (['contract-exists', 'job-completed'].includes(item.type)) {
15
- return true
16
- }
17
-
18
- if (item.type === 'basic-arithmetic') {
19
- const op = item.arguments?.operation
20
- return typeof op === 'string' && ['eq', 'neq', 'gt', 'lt', 'gte', 'lte'].includes(op)
21
- }
22
-
23
- return false
24
- }
25
-
26
- /**
27
- * Parses a YAML string defining a job into a structured `Job` object.
28
- * This function validates the presence and basic types of required fields
29
- * for both the job and its nested actions.
30
- *
31
- * @param yamlContent The raw YAML content of the job file as a string.
32
- * @returns The parsed and validated `Job` object.
33
- * @throws {Error} If the YAML is malformed or if the job is missing required fields.
34
- */
35
- export function parseJob(yamlContent: string): Job {
36
- let rawObject: any
37
- try {
38
- rawObject = parseYaml(yamlContent)
39
- } catch (e) {
40
- if (e instanceof YAMLParseError) {
41
- const line = e.linePos?.[0].line ? ` at line ${e.linePos[0].line}` : ''
42
- throw new Error(`Failed to parse job YAML: ${e.message}${line}.`)
43
- }
44
- throw e
45
- }
46
-
47
- if (!rawObject || typeof rawObject !== 'object') {
48
- throw new Error('Invalid job: YAML content must resolve to an object.')
49
- }
50
-
51
- // If a top-level discriminator exists and is not a job, bail out early with a helpful error
52
- if (rawObject.type && rawObject.type !== 'job') {
53
- throw new Error('Invalid job: unexpected type discriminator. Did you mean a template file with type: "template"?')
54
- }
55
-
56
- // --- Validate required top-level fields ---
57
- if (!rawObject.name || typeof rawObject.name !== 'string') {
58
- throw new Error('Invalid job: "name" field is required and must be a string.')
59
- }
60
- // The YAML parser may interpret a version like "1.0" as a number, so we ensure it's a string.
61
- if (!rawObject.version) {
62
- throw new Error(`Invalid job "${rawObject.name}": "version" field is required.`)
63
- }
64
- rawObject.version = String(rawObject.version)
65
-
66
- if (!rawObject.actions || !Array.isArray(rawObject.actions)) {
67
- throw new Error(`Invalid job "${rawObject.name}": "actions" field is required and must be an array.`)
68
- }
69
-
70
- // --- Validate each action within the job ---
71
- for (const action of rawObject.actions) {
72
- if (!action || typeof action !== 'object') {
73
- throw new Error(`Invalid job "${rawObject.name}": contains a non-object item in "actions" array.`)
74
- }
75
- if (!action.name || typeof action.name !== 'string') {
76
- throw new Error(`Invalid job "${rawObject.name}": an action is missing the required "name" field.`)
77
- }
78
-
79
- // Validate that the action has either a template or type field, but not both
80
- const hasTemplate = action.template && typeof action.template === 'string'
81
- const hasType = action.type && typeof action.type === 'string'
82
-
83
- if (!hasTemplate && !hasType) {
84
- throw new Error(`Invalid job "${rawObject.name}": action "${action.name}" must have either a "template" field (for template actions) or a "type" field (for primitive actions).`)
85
- }
86
- if (hasTemplate && hasType) {
87
- throw new Error(`Invalid job "${rawObject.name}": action "${action.name}" cannot have both "template" and "type" fields. Use only one.`)
88
- }
89
-
90
- if (!action.arguments || typeof action.arguments !== 'object' || Array.isArray(action.arguments)) {
91
- throw new Error(`Invalid job "${rawObject.name}": action "${action.name}" is missing the required "arguments" field or it is not an object.`)
92
- }
93
-
94
- // Validate the optional output field
95
- if (action.output !== undefined) {
96
- const t = typeof action.output
97
- const isObject = t === 'object' && action.output !== null && !Array.isArray(action.output)
98
- if (t !== 'boolean' && !isObject) {
99
- throw new Error(`Invalid job "${rawObject.name}": action "${action.name}" has an invalid "output" field. It must be either a boolean (true/false) or an object mapping custom outputs.`)
100
- }
101
- }
102
- }
103
-
104
- // --- Optional: validate deprecated flag if present ---
105
- if (rawObject.deprecated !== undefined && typeof rawObject.deprecated !== 'boolean') {
106
- throw new Error(`Invalid job "${rawObject.name}": "deprecated" must be a boolean if provided.`)
107
- }
108
- // --- Optional: validate min_evm_version if present ---
109
- if (rawObject.min_evm_version !== undefined && typeof rawObject.min_evm_version !== 'string') {
110
- throw new Error(`Invalid job "${rawObject.name}": "min_evm_version" must be a string if provided.`)
111
- }
112
-
113
- // --- Optional: validate job-level skip_condition and skip_if ---
114
- if (rawObject.skip_condition !== undefined) {
115
- if (!Array.isArray(rawObject.skip_condition)) {
116
- throw new Error(`Invalid job "${rawObject.name}": "skip_condition" must be an array if provided.`)
117
- }
118
- for (const condition of rawObject.skip_condition) {
119
- if (!isCondition(condition)) {
120
- throw new Error(`Invalid job "${rawObject.name}": "skip_condition" contains an invalid condition entry.`)
121
- }
122
- }
123
- }
124
-
125
- if (rawObject.skip_if !== undefined) {
126
- if (!Array.isArray(rawObject.skip_if)) {
127
- throw new Error(`Invalid job "${rawObject.name}": "skip_if" must be an array if provided.`)
128
- }
129
- for (const condition of rawObject.skip_if) {
130
- if (!isCondition(condition)) {
131
- throw new Error(`Invalid job "${rawObject.name}": "skip_if" contains an invalid condition entry.`)
132
- }
133
- }
134
- }
135
-
136
- if (rawObject.constants !== undefined) {
137
- if (typeof rawObject.constants !== 'object' || rawObject.constants === null || Array.isArray(rawObject.constants)) {
138
- throw new Error(`Invalid job "${rawObject.name}": "constants" field must be an object if provided.`)
139
- }
140
- }
141
-
142
- // --- Construct and return the strongly-typed Job object ---
143
- const job: Job = {
144
- name: rawObject.name,
145
- version: rawObject.version,
146
- description: rawObject.description,
147
- depends_on: rawObject.depends_on,
148
- // We've validated the necessary parts, so a cast is reasonable here.
149
- actions: rawObject.actions as JobAction[],
150
- only_networks: rawObject.only_networks,
151
- skip_networks: rawObject.skip_networks,
152
- min_evm_version: rawObject.min_evm_version,
153
- deprecated: rawObject.deprecated === true,
154
- skip_condition: rawObject.skip_condition as Condition[] | undefined,
155
- skip_if: rawObject.skip_if as Condition[] | undefined,
156
- constants: rawObject.constants,
157
- }
158
-
159
- return job
160
- }
@@ -1,129 +0,0 @@
1
- import { parse as parseYaml, YAMLParseError } from 'yaml'
2
- import { BuildInfoSourceProvenance, SourceDocument, SourceProvenance, SourceProvenanceOverride } from '../types'
3
-
4
- const STRING_FIELDS = ['repo', 'ref', 'commit', 'build'] as const
5
- const BUILD_INFO_FIELDS = new Set<string>([...STRING_FIELDS, 'contracts'])
6
- const CONTRACT_OVERRIDE_FIELDS = new Set<string>(STRING_FIELDS)
7
-
8
- function isPlainObject(value: any): value is Record<string, any> {
9
- return typeof value === 'object' && value !== null && !Array.isArray(value)
10
- }
11
-
12
- function buildInfoLabel(buildInfoPath: string): string {
13
- return `build_info[${JSON.stringify(buildInfoPath)}]`
14
- }
15
-
16
- function contractOverrideLabel(buildInfoPath: string, contractName: string): string {
17
- return `${buildInfoLabel(buildInfoPath)}.contracts[${JSON.stringify(contractName)}]`
18
- }
19
-
20
- function validateKnownFields(value: Record<string, any>, label: string, allowedFields: Set<string>): void {
21
- for (const field of Object.keys(value)) {
22
- if (!allowedFields.has(field)) {
23
- throw new Error(`Invalid source: ${label}.${field} is not supported.`)
24
- }
25
- }
26
- }
27
-
28
- function validateStringFields(value: Record<string, any>, label: string, requiredRepo: boolean): void {
29
- if (requiredRepo && (typeof value.repo !== 'string' || value.repo.length === 0)) {
30
- throw new Error(`Invalid source: ${label}.repo field is required and must be a non-empty string.`)
31
- }
32
-
33
- for (const field of STRING_FIELDS) {
34
- if (value[field] !== undefined && typeof value[field] !== 'string') {
35
- throw new Error(`Invalid source: ${label}.${field} must be a string if provided.`)
36
- }
37
- }
38
- }
39
-
40
- function validateBuildInfoProvenance(buildInfoPath: string, provenance: unknown): BuildInfoSourceProvenance {
41
- if (!isPlainObject(provenance)) {
42
- throw new Error(`Invalid source: ${buildInfoLabel(buildInfoPath)} must be an object.`)
43
- }
44
-
45
- const label = buildInfoLabel(buildInfoPath)
46
- validateKnownFields(provenance, label, BUILD_INFO_FIELDS)
47
- validateStringFields(provenance, label, true)
48
-
49
- if (provenance.contracts !== undefined) {
50
- if (!isPlainObject(provenance.contracts)) {
51
- throw new Error(`Invalid source: ${label}.contracts must be an object if provided.`)
52
- }
53
-
54
- for (const [contractName, override] of Object.entries(provenance.contracts)) {
55
- if (!contractName || typeof contractName !== 'string') {
56
- throw new Error(`Invalid source: ${label}.contracts keys must be non-empty strings.`)
57
- }
58
- if (!isPlainObject(override)) {
59
- throw new Error(`Invalid source: ${contractOverrideLabel(buildInfoPath, contractName)} must be an object.`)
60
- }
61
- const overrideLabel = contractOverrideLabel(buildInfoPath, contractName)
62
- validateKnownFields(override, overrideLabel, CONTRACT_OVERRIDE_FIELDS)
63
- validateStringFields(override, overrideLabel, false)
64
- }
65
- }
66
-
67
- return provenance as BuildInfoSourceProvenance
68
- }
69
-
70
- /**
71
- * Parses a YAML source provenance document.
72
- * Returns null for YAML documents that are not marked with `type: "source"`.
73
- */
74
- export function parseSourceDocument(yamlContent: string): SourceDocument | null {
75
- let rawObject: any
76
- try {
77
- rawObject = parseYaml(yamlContent)
78
- } catch (e) {
79
- if (e instanceof YAMLParseError) {
80
- const line = (e as any).linePos?.[0]?.line ? ` at line ${(e as any).linePos[0].line}` : ''
81
- throw new Error(`Failed to parse source YAML: ${e.message}${line}.`)
82
- }
83
- throw e
84
- }
85
-
86
- if (!rawObject || typeof rawObject !== 'object') {
87
- return null
88
- }
89
-
90
- if (rawObject.type !== 'source') {
91
- return null
92
- }
93
-
94
- if (!isPlainObject(rawObject.build_info)) {
95
- throw new Error('Invalid source: "build_info" field is required and must be an object.')
96
- }
97
-
98
- const buildInfo: Record<string, BuildInfoSourceProvenance> = {}
99
- const warnings: string[] = []
100
-
101
- for (const [buildInfoPath, provenance] of Object.entries(rawObject.build_info)) {
102
- if (!buildInfoPath || typeof buildInfoPath !== 'string') {
103
- throw new Error('Invalid source: "build_info" keys must be non-empty strings.')
104
- }
105
-
106
- try {
107
- buildInfo[buildInfoPath] = validateBuildInfoProvenance(buildInfoPath, provenance)
108
- } catch (error) {
109
- warnings.push(error instanceof Error ? error.message : String(error))
110
- }
111
- }
112
-
113
- return {
114
- type: 'source',
115
- build_info: buildInfo,
116
- warnings
117
- }
118
- }
119
-
120
- export function mergeSourceProvenance(
121
- base: BuildInfoSourceProvenance,
122
- override?: SourceProvenanceOverride
123
- ): SourceProvenance {
124
- const { contracts, ...baseFields } = base
125
- return {
126
- ...baseFields,
127
- ...(override || {})
128
- }
129
- }
@@ -1,135 +0,0 @@
1
- import { parse as parseYaml, YAMLParseError } from 'yaml'
2
- import { Template, Action, Condition, Value } from '../types'
3
-
4
- /**
5
- * Helper to check if a parsed YAML object represents a Condition.
6
- * Conditions are specific checks that resolve to a boolean, used in `skip_condition` blocks.
7
- * @param item The parsed object from YAML.
8
- * @returns True if the item is a Condition, false otherwise.
9
- */
10
- function isCondition(item: any): item is Condition {
11
- // A 'Condition' must be an object with a 'type' property.
12
- if (!item || typeof item !== 'object' || typeof item.type !== 'string') {
13
- return false
14
- }
15
-
16
- // Check against known, specific condition types from `src/lib/types/conditions.ts`.
17
- if (['contract-exists', 'job-completed'].includes(item.type)) {
18
- return true
19
- }
20
-
21
- if (item.type === 'value-empty') {
22
- return !!(item.arguments && typeof item.arguments === 'object' && 'value' in item.arguments)
23
- }
24
-
25
- // The 'basic-arithmetic' ValueResolver can also act as a Condition
26
- // if it performs a boolean comparison operation.
27
- if (item.type === 'basic-arithmetic') {
28
- if (item.arguments && typeof item.arguments.operation === 'string') {
29
- const booleanOperations = ['eq', 'neq', 'gt', 'lt', 'gte', 'lte']
30
- return booleanOperations.includes(item.arguments.operation)
31
- }
32
- }
33
-
34
- return false
35
- }
36
-
37
- /**
38
- * Parses a YAML string defining an action template into a structured `Template` object.
39
- * This function handles validation and normalization of the template structure, including
40
- * the flexible `setup` block which can be an array of mixed actions/conditions or
41
- * a structured object.
42
- *
43
- * @param yamlContent The raw YAML content of the template file as a string.
44
- * @returns The parsed and validated `Template` object.
45
- * @throws {Error} If the YAML is malformed, or if the template is missing required fields
46
- * like `name`, `actions`, or `outputs`.
47
- */
48
- export function parseTemplate(yamlContent: string): Template {
49
- let rawObject: any
50
- try {
51
- rawObject = parseYaml(yamlContent)
52
- } catch (e) {
53
- if (e instanceof YAMLParseError) {
54
- // Provide a more user-friendly error message including the line number
55
- const line = e.linePos?.[0].line ? ` at line ${e.linePos[0].line}` : ''
56
- throw new Error(`Failed to parse template YAML: ${e.message}${line}.`)
57
- }
58
- throw e
59
- }
60
-
61
- if (!rawObject || typeof rawObject !== 'object') {
62
- throw new Error('Invalid template: YAML content must resolve to an object.')
63
- }
64
-
65
- // If a discriminator is provided, ensure it matches
66
- if (rawObject.type !== undefined && rawObject.type !== 'template') {
67
- throw new Error('Invalid template: expected type to be "template" if provided.')
68
- }
69
-
70
- // --- Validate required fields ---
71
- if (!rawObject.name || typeof rawObject.name !== 'string') {
72
- throw new Error('Invalid template: "name" field is required and must be a string.')
73
- }
74
- if (!rawObject.actions || !Array.isArray(rawObject.actions)) {
75
- throw new Error(`Invalid template "${rawObject.name}": "actions" field is required and must be an array.`)
76
- }
77
-
78
- // Allow 'outputs' to be optional. If it exists, it must be an object.
79
- if (rawObject.outputs && (typeof rawObject.outputs !== 'object' || Array.isArray(rawObject.outputs))) {
80
- throw new Error(`Invalid template "${rawObject.name}": "outputs" field must be an object if provided.`)
81
- }
82
-
83
- // --- Construct the base template object ---
84
- const template: Template = {
85
- type: 'template',
86
- name: rawObject.name,
87
- description: rawObject.description,
88
- arguments: rawObject.arguments,
89
- returns: rawObject.returns,
90
- actions: rawObject.actions as Action[],
91
- skip_condition: rawObject.skip_condition as Condition[],
92
- }
93
-
94
- // Only include outputs if it was provided in the YAML
95
- if (rawObject.outputs) {
96
- template.outputs = rawObject.outputs as Record<string, Value<any>>
97
- }
98
-
99
- // --- Handle the 'setup' block which can have multiple formats ---
100
- if (rawObject.setup) {
101
- if (Array.isArray(rawObject.setup)) {
102
- // Format 1: An array of mixed actions and conditions (e.g., sequence-factory-v1.yaml).
103
- // We need to iterate and categorize each item.
104
- const setupActions: Action[] = []
105
- const setupConditions: Condition[] = []
106
-
107
- for (const item of rawObject.setup) {
108
- if (isCondition(item)) {
109
- setupConditions.push(item)
110
- } else {
111
- // If it's not an explicit condition, we assume it's an action.
112
- setupActions.push(item as Action)
113
- }
114
- }
115
-
116
- template.setup = {}
117
- if (setupActions.length > 0) {
118
- template.setup.actions = setupActions
119
- }
120
- if (setupConditions.length > 0) {
121
- template.setup.skip_condition = setupConditions
122
- }
123
- } else if (typeof rawObject.setup === 'object') {
124
- // Format 2: A structured object (e.g., nano-universal-deployer.yaml), which maps directly to our type.
125
- template.setup = {
126
- actions: (rawObject.setup.actions || []) as Action[],
127
- skip_condition: (rawObject.setup.skip_condition || []) as Condition[],
128
- }
129
- } else {
130
- throw new Error(`Invalid template "${rawObject.name}": "setup" field must be an array or an object if provided.`)
131
- }
132
- }
133
-
134
- return template
135
- }