@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.
- package/README.md +27 -0
- package/dist/lib/__tests__/network-loader.spec.js.map +1 -1
- package/dist/lib/core/__tests__/resolver.spec.js +22 -0
- package/dist/lib/core/__tests__/resolver.spec.js.map +1 -1
- package/dist/lib/core/__tests__/sign-actions.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/sign-actions.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/sign-actions.spec.js +128 -0
- package/dist/lib/core/__tests__/sign-actions.spec.js.map +1 -0
- package/dist/lib/core/__tests__/signer.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/signer.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/signer.spec.js +40 -0
- package/dist/lib/core/__tests__/signer.spec.js.map +1 -0
- package/dist/lib/core/context.d.ts +3 -2
- package/dist/lib/core/context.d.ts.map +1 -1
- package/dist/lib/core/context.js +3 -2
- package/dist/lib/core/context.js.map +1 -1
- package/dist/lib/core/engine.d.ts +4 -0
- package/dist/lib/core/engine.d.ts.map +1 -1
- package/dist/lib/core/engine.js +173 -0
- package/dist/lib/core/engine.js.map +1 -1
- package/dist/lib/core/signer.d.ts +7 -0
- package/dist/lib/core/signer.d.ts.map +1 -0
- package/dist/lib/core/signer.js +60 -0
- package/dist/lib/core/signer.js.map +1 -0
- package/dist/lib/parsers/__tests__/source.spec.js +37 -0
- package/dist/lib/parsers/__tests__/source.spec.js.map +1 -1
- package/dist/lib/parsers/source.js +1 -1
- package/dist/lib/parsers/source.js.map +1 -1
- package/dist/lib/provenance.js +51 -2
- package/dist/lib/provenance.js.map +1 -1
- package/dist/lib/types/actions.d.ts +26 -2
- package/dist/lib/types/actions.d.ts.map +1 -1
- package/dist/lib/types/actions.js +3 -0
- package/dist/lib/types/actions.js.map +1 -1
- package/dist/lib/types/source.d.ts +2 -0
- package/dist/lib/types/source.d.ts.map +1 -1
- package/package.json +4 -1
- package/.eslintrc.json +0 -29
- package/.github/workflows/ci.yml +0 -181
- package/CONCEPT.md +0 -24
- package/contracts/checked-call.huff +0 -65
- package/eslint.config.js +0 -48
- package/examples/jobs/guards-v1.yaml +0 -17
- package/examples/jobs/sequence-seq-0001-patch.yaml +0 -59
- package/examples/jobs/sequence-v1.yaml +0 -59
- package/examples/templates/sequence-factory-v1.yaml +0 -56
- package/jest.config.js +0 -25
- package/src/cli.ts +0 -18
- package/src/commands/common.ts +0 -61
- package/src/commands/dry.ts +0 -209
- package/src/commands/etherscan.ts +0 -360
- package/src/commands/index.ts +0 -6
- package/src/commands/list.ts +0 -262
- package/src/commands/provenance.ts +0 -120
- package/src/commands/run.ts +0 -146
- package/src/commands/utils.ts +0 -215
- package/src/index.ts +0 -67
- package/src/lib/__tests__/deployer-events.spec.ts +0 -338
- package/src/lib/__tests__/deployer.spec.ts +0 -2269
- package/src/lib/__tests__/network-loader.spec.ts +0 -150
- package/src/lib/__tests__/network-selection.spec.ts +0 -41
- package/src/lib/__tests__/network-utils.spec.ts +0 -230
- package/src/lib/__tests__/provenance.spec.ts +0 -208
- package/src/lib/artifacts/__tests__/fixtures/contract1.json +0 -19
- package/src/lib/artifacts/__tests__/fixtures/contract2.json +0 -19
- package/src/lib/artifacts/__tests__/fixtures/duplicate-name.json +0 -19
- package/src/lib/artifacts/__tests__/fixtures/nested/nested-contract.json +0 -18
- package/src/lib/artifacts/__tests__/fixtures/not-an-artifact.json +0 -8
- package/src/lib/artifacts/__tests__/fixtures/readme.txt +0 -2
- package/src/lib/contracts/__tests__/repository.spec.ts +0 -612
- package/src/lib/contracts/repository.ts +0 -411
- package/src/lib/core/__tests__/assert-action.spec.ts +0 -474
- package/src/lib/core/__tests__/context.spec.ts +0 -37
- package/src/lib/core/__tests__/engine.spec.ts +0 -2005
- package/src/lib/core/__tests__/graph.spec.ts +0 -125
- package/src/lib/core/__tests__/json-integration.spec.ts +0 -425
- package/src/lib/core/__tests__/loader.spec.ts +0 -367
- package/src/lib/core/__tests__/multi-platform-verification.spec.ts +0 -406
- package/src/lib/core/__tests__/resolver.spec.ts +0 -2496
- package/src/lib/core/__tests__/static-action.spec.ts +0 -172
- package/src/lib/core/context.ts +0 -127
- package/src/lib/core/engine.ts +0 -1834
- package/src/lib/core/graph.ts +0 -252
- package/src/lib/core/loader.ts +0 -253
- package/src/lib/core/resolver.ts +0 -873
- package/src/lib/deployer.ts +0 -1005
- package/src/lib/events/__tests__/event-system.spec.ts +0 -392
- package/src/lib/events/cli-adapter.ts +0 -369
- package/src/lib/events/emitter.ts +0 -62
- package/src/lib/events/index.ts +0 -3
- package/src/lib/events/types.ts +0 -520
- package/src/lib/index.ts +0 -17
- package/src/lib/network-loader.ts +0 -90
- package/src/lib/network-selection.ts +0 -73
- package/src/lib/network-utils.ts +0 -64
- package/src/lib/parsers/__tests__/buildinfo.spec.ts +0 -122
- package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-bytecode-buildinfo.json +0 -62
- package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-json.txt +0 -2
- package/src/lib/parsers/__tests__/fixtures/buildinfo/multi-contract-buildinfo.json +0 -89
- package/src/lib/parsers/__tests__/fixtures/buildinfo/no-contracts-buildinfo.json +0 -17
- package/src/lib/parsers/__tests__/fixtures/buildinfo/simple-buildinfo.json +0 -63
- package/src/lib/parsers/__tests__/fixtures/buildinfo/wrong-format.json +0 -4
- package/src/lib/parsers/__tests__/job.spec.ts +0 -439
- package/src/lib/parsers/__tests__/source.spec.ts +0 -134
- package/src/lib/parsers/__tests__/template.spec.ts +0 -111
- package/src/lib/parsers/artifact/__tests__/artifact.spec.ts +0 -117
- package/src/lib/parsers/artifact/__tests__/fixtures/empty-bytecode.json +0 -5
- package/src/lib/parsers/artifact/__tests__/fixtures/hardhat-artifact.json +0 -67
- package/src/lib/parsers/artifact/__tests__/fixtures/invalid-bytecode.json +0 -5
- package/src/lib/parsers/artifact/__tests__/fixtures/invalid-json.txt +0 -11
- package/src/lib/parsers/artifact/__tests__/fixtures/minimal-artifact.json +0 -5
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-abi.json +0 -4
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-bytecode.json +0 -11
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-contract-name.json +0 -11
- package/src/lib/parsers/artifact/__tests__/fixtures/simple-artifact.json +0 -40
- package/src/lib/parsers/artifact/__tests__/fixtures/wrong-types.json +0 -7
- package/src/lib/parsers/artifact/foundry-1.2.ts +0 -72
- package/src/lib/parsers/artifact/index.ts +0 -27
- package/src/lib/parsers/artifact/types.ts +0 -9
- package/src/lib/parsers/buildinfo.ts +0 -127
- package/src/lib/parsers/constants.ts +0 -56
- package/src/lib/parsers/index.ts +0 -6
- package/src/lib/parsers/job.ts +0 -160
- package/src/lib/parsers/source.ts +0 -129
- package/src/lib/parsers/template.ts +0 -135
- package/src/lib/provenance.ts +0 -785
- package/src/lib/std/templates/arachnid-deterministic-deployment-proxy.yaml +0 -68
- package/src/lib/std/templates/assured-deployment.yaml +0 -46
- package/src/lib/std/templates/era-evm-predeploy.yaml +0 -35
- package/src/lib/std/templates/erc-2470.yaml +0 -70
- package/src/lib/std/templates/min-balance.yaml +0 -35
- package/src/lib/std/templates/nano-universal-deployer.yaml +0 -61
- package/src/lib/std/templates/raw-erc-2470.yaml +0 -62
- package/src/lib/std/templates/raw-nano-universal-deployer.yaml +0 -54
- package/src/lib/std/templates/raw-sequence-universal-deployer-2.yaml +0 -52
- package/src/lib/std/templates/sequence-universal-deployer-2.yaml +0 -61
- package/src/lib/types/__tests__/json-request-action.spec.ts +0 -243
- package/src/lib/types/__tests__/read-json-value.spec.ts +0 -278
- package/src/lib/types/__tests__/resolve-json-value.spec.ts +0 -769
- package/src/lib/types/actions.ts +0 -148
- package/src/lib/types/artifacts.ts +0 -21
- package/src/lib/types/buildinfo.ts +0 -116
- package/src/lib/types/conditions.ts +0 -50
- package/src/lib/types/contracts.ts +0 -26
- package/src/lib/types/definitions.ts +0 -77
- package/src/lib/types/index.ts +0 -9
- package/src/lib/types/network.ts +0 -33
- package/src/lib/types/project.ts +0 -9
- package/src/lib/types/source.ts +0 -26
- package/src/lib/types/task.ts +0 -9
- package/src/lib/types/values.ts +0 -221
- package/src/lib/utils/assertion.ts +0 -24
- package/src/lib/utils/validation.ts +0 -116
- package/src/lib/validation/contract-references.ts +0 -210
- package/src/lib/validation/index.ts +0 -1
- package/src/lib/verification/__tests__/etherscan.spec.ts +0 -710
- package/src/lib/verification/__tests__/sourcify.spec.ts +0 -288
- package/src/lib/verification/etherscan.ts +0 -547
- package/src/lib/verification/sourcify.ts +0 -248
- package/test_validation/artifacts/TestContract.json +0 -9
- package/test_validation/jobs/test-missing.yaml +0 -16
- package/test_validation/networks.yaml +0 -3
- package/tsconfig.json +0 -36
package/src/lib/parsers/job.ts
DELETED
|
@@ -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
|
-
}
|