@openfn/project 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.d.ts +23 -5
- package/dist/index.js +115 -38
- package/dist/workflow.ohm +38 -0
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -51,10 +51,10 @@ a-b
|
|
|
51
51
|
a-c
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
You can set properties on the workflow itself - probably the
|
|
54
|
+
You can set properties on the workflow itself - probably the id, with `@attributes`
|
|
55
55
|
|
|
56
56
|
```
|
|
57
|
-
@
|
|
57
|
+
@id my-cool-workflow
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
You can also set properties on a node by putting comma seperated key-value pairs in brackets
|
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ declare class Workflow {
|
|
|
15
15
|
uuid: {};
|
|
16
16
|
id: {};
|
|
17
17
|
};
|
|
18
|
-
name
|
|
18
|
+
name?: string;
|
|
19
19
|
id: string;
|
|
20
20
|
openfn: OpenfnMeta;
|
|
21
21
|
constructor(workflow: l.Workflow);
|
|
@@ -31,6 +31,7 @@ declare class Workflow {
|
|
|
31
31
|
}) | undefined;
|
|
32
32
|
getUUID(id: any): string;
|
|
33
33
|
toJSON(): JSON.Object;
|
|
34
|
+
getUUIDMap(): Record<string, string>;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
type FromFsConfig = {
|
|
@@ -47,6 +48,10 @@ type FileFormats = 'yaml' | 'json';
|
|
|
47
48
|
interface OpenfnConfig {
|
|
48
49
|
name: string;
|
|
49
50
|
workflowRoot: string;
|
|
51
|
+
dirs: {
|
|
52
|
+
workflows: string;
|
|
53
|
+
projects: string;
|
|
54
|
+
};
|
|
50
55
|
formats: {
|
|
51
56
|
openfn: FileFormats;
|
|
52
57
|
project: FileFormats;
|
|
@@ -82,7 +87,9 @@ declare class Project {
|
|
|
82
87
|
collections: any;
|
|
83
88
|
static from(type: 'state', data: any, options: Partial<l.ProjectConfig>): Project;
|
|
84
89
|
static from(type: 'fs', options: FromFsConfig): Project;
|
|
85
|
-
static from(type: 'path', data:
|
|
90
|
+
static from(type: 'path', data: string, options?: {
|
|
91
|
+
config?: Partial<OpenfnConfig>;
|
|
92
|
+
}): Project;
|
|
86
93
|
static diff(a: Project, b: Project): void;
|
|
87
94
|
static merge(source: Project, target: Project, options: MergeProjectOptions): Project;
|
|
88
95
|
constructor(data: l.Project, repoConfig?: RepoOptions);
|
|
@@ -92,6 +99,13 @@ declare class Project {
|
|
|
92
99
|
getIdentifier(): string;
|
|
93
100
|
compare(proj: Project): void;
|
|
94
101
|
getUUID(workflow: string | Workflow, stepId: string, otherStep?: string): any;
|
|
102
|
+
/**
|
|
103
|
+
* Returns a map of ids:uuids for everything in the project
|
|
104
|
+
*/
|
|
105
|
+
getUUIDMap(options?: {
|
|
106
|
+
workflows: boolean;
|
|
107
|
+
project: false;
|
|
108
|
+
}): {};
|
|
95
109
|
}
|
|
96
110
|
|
|
97
111
|
declare class Workspace {
|
|
@@ -104,7 +118,7 @@ declare class Workspace {
|
|
|
104
118
|
get(id: string): Project | undefined;
|
|
105
119
|
getProjectPath(id: string): string | undefined;
|
|
106
120
|
getActiveProject(): Project | undefined;
|
|
107
|
-
getConfig(): OpenfnConfig
|
|
121
|
+
getConfig(): Partial<OpenfnConfig>;
|
|
108
122
|
get activeProjectId(): string | undefined;
|
|
109
123
|
get valid(): boolean;
|
|
110
124
|
}
|
|
@@ -115,14 +129,18 @@ declare function jsonToYaml(json: string | JSONObject): string;
|
|
|
115
129
|
type GenerateWorkflowOptions = {
|
|
116
130
|
name: string;
|
|
117
131
|
uuidSeed: number;
|
|
118
|
-
openfnUuid: boolean;
|
|
119
132
|
printErrors: boolean;
|
|
133
|
+
uuidMap?: Record<string, string>;
|
|
134
|
+
openfnUuid: boolean;
|
|
135
|
+
};
|
|
136
|
+
type GenerateProjectOptions = GenerateWorkflowOptions & {
|
|
137
|
+
uuidMap: Array<Record<string, string>>;
|
|
120
138
|
};
|
|
121
139
|
/**
|
|
122
140
|
* Generate a Workflow from a simple text based representation
|
|
123
141
|
* eg, `a-b b-c a-c`
|
|
124
142
|
*/
|
|
125
143
|
declare function generateWorkflow(def: string, options?: Partial<GenerateWorkflowOptions>): Workflow;
|
|
126
|
-
declare function generateProject(name: string, workflowDefs: string[], options
|
|
144
|
+
declare function generateProject(name: string, workflowDefs: string[], options?: Partial<GenerateProjectOptions>): Project;
|
|
127
145
|
|
|
128
146
|
export { Workspace, Project as default, generateProject, generateWorkflow, jsonToYaml, yamlToJson };
|
package/dist/index.js
CHANGED
|
@@ -31,8 +31,15 @@ var Workflow = class {
|
|
|
31
31
|
};
|
|
32
32
|
this.workflow = clone(workflow);
|
|
33
33
|
const { id, name, openfn, steps, ...options } = workflow;
|
|
34
|
+
if (!(id || name)) {
|
|
35
|
+
throw new Error("A Workflow MUST have a name or id");
|
|
36
|
+
}
|
|
34
37
|
this.id = id ?? slugify(name);
|
|
35
|
-
this.name = name
|
|
38
|
+
this.name = name;
|
|
39
|
+
this.workflow.id = this.id;
|
|
40
|
+
if (name) {
|
|
41
|
+
this.workflow.name = this.name;
|
|
42
|
+
}
|
|
36
43
|
this.openfn = openfn;
|
|
37
44
|
this.options = options;
|
|
38
45
|
this.#buildIndex();
|
|
@@ -128,6 +135,9 @@ var Workflow = class {
|
|
|
128
135
|
toJSON() {
|
|
129
136
|
return this.workflow;
|
|
130
137
|
}
|
|
138
|
+
getUUIDMap() {
|
|
139
|
+
return this.index.uuid;
|
|
140
|
+
}
|
|
131
141
|
};
|
|
132
142
|
var Workflow_default = Workflow;
|
|
133
143
|
|
|
@@ -157,7 +167,7 @@ function to_json_default(project) {
|
|
|
157
167
|
}
|
|
158
168
|
|
|
159
169
|
// src/util/rename-keys.ts
|
|
160
|
-
function renameKeys(props, keyMap) {
|
|
170
|
+
function renameKeys(props = {}, keyMap) {
|
|
161
171
|
return Object.fromEntries(
|
|
162
172
|
Object.entries(props).map(([key, value]) => [
|
|
163
173
|
keyMap[key] ? keyMap[key] : key,
|
|
@@ -183,7 +193,7 @@ function jsonToYaml(json) {
|
|
|
183
193
|
// src/serialize/to-app-state.ts
|
|
184
194
|
import { randomUUID } from "node:crypto";
|
|
185
195
|
function to_app_state_default(project, options = {}) {
|
|
186
|
-
const { uuid: id, endpoint, env, ...rest } = project.openfn;
|
|
196
|
+
const { uuid: id, endpoint, env, ...rest } = project.openfn ?? {};
|
|
187
197
|
const state = {
|
|
188
198
|
id,
|
|
189
199
|
name: project.name,
|
|
@@ -204,9 +214,11 @@ var mapWorkflow = (workflow) => {
|
|
|
204
214
|
if (workflow instanceof Workflow_default) {
|
|
205
215
|
workflow = workflow.toJSON();
|
|
206
216
|
}
|
|
217
|
+
const { uuid, ...originalOpenfnProps } = workflow.openfn ?? {};
|
|
207
218
|
const wfState = {
|
|
219
|
+
...originalOpenfnProps,
|
|
220
|
+
id: workflow.openfn?.uuid ?? randomUUID(),
|
|
208
221
|
name: workflow.name,
|
|
209
|
-
...renameKeys(workflow.openfn, { uuid: "id" }),
|
|
210
222
|
jobs: [],
|
|
211
223
|
triggers: [],
|
|
212
224
|
edges: []
|
|
@@ -349,10 +361,12 @@ function slugify2(text) {
|
|
|
349
361
|
return text.replace(/\W/g, " ").trim().replace(/\s+/g, "-").toLowerCase();
|
|
350
362
|
}
|
|
351
363
|
var from_app_state_default = (state, config) => {
|
|
352
|
-
if (
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
364
|
+
if (typeof state === "string") {
|
|
365
|
+
if (config?.format === "yaml") {
|
|
366
|
+
state = yamlToJson(state);
|
|
367
|
+
} else {
|
|
368
|
+
state = JSON.parse(state);
|
|
369
|
+
}
|
|
356
370
|
}
|
|
357
371
|
const {
|
|
358
372
|
id,
|
|
@@ -367,13 +381,11 @@ var from_app_state_default = (state, config) => {
|
|
|
367
381
|
} = state;
|
|
368
382
|
const proj = {
|
|
369
383
|
name,
|
|
370
|
-
// TODO do we need to slug this or anything?
|
|
371
384
|
description,
|
|
372
385
|
collections,
|
|
373
386
|
credentials,
|
|
374
387
|
options
|
|
375
388
|
};
|
|
376
|
-
const repoConfig = {};
|
|
377
389
|
proj.openfn = {
|
|
378
390
|
uuid: id,
|
|
379
391
|
endpoint: config.endpoint,
|
|
@@ -385,7 +397,7 @@ var from_app_state_default = (state, config) => {
|
|
|
385
397
|
fetched_at: config.fetchedAt
|
|
386
398
|
};
|
|
387
399
|
proj.workflows = state.workflows.map(mapWorkflow2);
|
|
388
|
-
return new Project(proj,
|
|
400
|
+
return new Project(proj, config?.repo);
|
|
389
401
|
};
|
|
390
402
|
var mapTriggerEdgeCondition = (edge) => {
|
|
391
403
|
const e = {
|
|
@@ -406,11 +418,13 @@ var mapTriggerEdgeCondition = (edge) => {
|
|
|
406
418
|
var mapWorkflow2 = (workflow) => {
|
|
407
419
|
const { jobs, edges, triggers, name, ...remoteProps } = workflow;
|
|
408
420
|
const mapped = {
|
|
409
|
-
id: slugify2(workflow.name),
|
|
410
421
|
name: workflow.name,
|
|
411
422
|
steps: [],
|
|
412
423
|
openfn: renameKeys(remoteProps, { id: "uuid" })
|
|
413
424
|
};
|
|
425
|
+
if (workflow.name) {
|
|
426
|
+
mapped.id = slugify2(workflow.name);
|
|
427
|
+
}
|
|
414
428
|
workflow.triggers.forEach((trigger) => {
|
|
415
429
|
const { type, ...otherProps } = trigger;
|
|
416
430
|
const connectedEdges = edges.filter(
|
|
@@ -454,6 +468,30 @@ var mapWorkflow2 = (workflow) => {
|
|
|
454
468
|
return mapped;
|
|
455
469
|
};
|
|
456
470
|
|
|
471
|
+
// src/parse/from-path.ts
|
|
472
|
+
import { extname } from "node:path";
|
|
473
|
+
import { readFile } from "node:fs/promises";
|
|
474
|
+
var from_path_default = async (path4, options = {}) => {
|
|
475
|
+
const ext = extname(path4).toLowerCase();
|
|
476
|
+
const source = await readFile(path4, "utf8");
|
|
477
|
+
const config = {
|
|
478
|
+
format: null,
|
|
479
|
+
repo: options.repo ?? options.config
|
|
480
|
+
// TMP
|
|
481
|
+
};
|
|
482
|
+
let state;
|
|
483
|
+
if (ext === ".json") {
|
|
484
|
+
config.format = "json";
|
|
485
|
+
state = JSON.parse(source);
|
|
486
|
+
} else if (ext.match(/(ya?ml)$/)) {
|
|
487
|
+
config.format = "yaml";
|
|
488
|
+
state = yamlToJson(source);
|
|
489
|
+
} else {
|
|
490
|
+
throw new Error(`Cannot load a project from a ${ext} file`);
|
|
491
|
+
}
|
|
492
|
+
return from_app_state_default(state, config);
|
|
493
|
+
};
|
|
494
|
+
|
|
457
495
|
// src/parse/from-fs.ts
|
|
458
496
|
import fs from "node:fs/promises";
|
|
459
497
|
import path from "node:path";
|
|
@@ -501,8 +539,12 @@ var parseProject = async (options = {}) => {
|
|
|
501
539
|
env: config.project?.env
|
|
502
540
|
});
|
|
503
541
|
try {
|
|
504
|
-
const format = config.formats
|
|
505
|
-
const statePath = path.join(
|
|
542
|
+
const format = config.formats?.project ?? config.formats?.projects ?? "yaml";
|
|
543
|
+
const statePath = path.join(
|
|
544
|
+
root,
|
|
545
|
+
config.dirs?.projects ?? ".projects",
|
|
546
|
+
`${identifier}.${format}`
|
|
547
|
+
);
|
|
506
548
|
const stateFile = await fs.readFile(statePath, "utf8");
|
|
507
549
|
state = from_app_state_default(stateFile, { format });
|
|
508
550
|
} catch (e) {
|
|
@@ -510,7 +552,8 @@ var parseProject = async (options = {}) => {
|
|
|
510
552
|
}
|
|
511
553
|
const { project: openfn, ...repo } = config;
|
|
512
554
|
proj.openfn = openfn;
|
|
513
|
-
|
|
555
|
+
proj.config = repo;
|
|
556
|
+
const workflowDir = config.workflowRoot ?? config.dirs?.workflows ?? "workflows";
|
|
514
557
|
const fileType = config.formats?.workflow ?? "yaml";
|
|
515
558
|
const pattern = `${root}/${workflowDir}/*/*.${fileType}`;
|
|
516
559
|
const candidateWfs = await glob(pattern, {
|
|
@@ -527,7 +570,6 @@ var parseProject = async (options = {}) => {
|
|
|
527
570
|
uuid: wfState.openfn?.uuid ?? null
|
|
528
571
|
// TODO do we need to transfer more stuff?
|
|
529
572
|
};
|
|
530
|
-
console.log("Loading workflow at ", filePath);
|
|
531
573
|
for (const step of wf.steps) {
|
|
532
574
|
if (step.expression && step.expression.endsWith(".js")) {
|
|
533
575
|
const dir = path.dirname(filePath);
|
|
@@ -565,7 +607,9 @@ var parseProject = async (options = {}) => {
|
|
|
565
607
|
var getUuidForStep = (project, workflow, stepId) => {
|
|
566
608
|
const wf = typeof workflow === "string" ? project.getWorkflow(workflow) : workflow;
|
|
567
609
|
if (!wf) {
|
|
568
|
-
throw new Error(
|
|
610
|
+
throw new Error(
|
|
611
|
+
`Workflow "${workflow}" not found in project ${project.id}`
|
|
612
|
+
);
|
|
569
613
|
}
|
|
570
614
|
for (const step of wf.steps) {
|
|
571
615
|
if (step.id === stepId) {
|
|
@@ -950,6 +994,7 @@ function merge(source, target, options) {
|
|
|
950
994
|
// src/Project.ts
|
|
951
995
|
var maybeCreateWorkflow = (wf) => wf instanceof Workflow_default ? wf : new Workflow_default(wf);
|
|
952
996
|
var setConfigDefaults = (config = {}) => ({
|
|
997
|
+
...config,
|
|
953
998
|
workflowRoot: config.workflowRoot ?? "workflows",
|
|
954
999
|
formats: {
|
|
955
1000
|
// TODO change these maybe
|
|
@@ -976,7 +1021,7 @@ var Project = class {
|
|
|
976
1021
|
meta;
|
|
977
1022
|
// this contains meta about the connected openfn project
|
|
978
1023
|
openfn;
|
|
979
|
-
//
|
|
1024
|
+
// workspace-wide configuration options
|
|
980
1025
|
// these should be shared across projects
|
|
981
1026
|
// and saved to an openfn.yaml file
|
|
982
1027
|
repo;
|
|
@@ -991,6 +1036,8 @@ var Project = class {
|
|
|
991
1036
|
return from_app_state_default(data, options);
|
|
992
1037
|
} else if (type === "fs") {
|
|
993
1038
|
return parseProject(data, options);
|
|
1039
|
+
} else if (type === "path") {
|
|
1040
|
+
return from_path_default(data, options);
|
|
994
1041
|
}
|
|
995
1042
|
throw new Error(`Didn't recognize type ${type}`);
|
|
996
1043
|
}
|
|
@@ -1052,6 +1099,19 @@ var Project = class {
|
|
|
1052
1099
|
}
|
|
1053
1100
|
return getUuidForStep(this, workflow, stepId);
|
|
1054
1101
|
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Returns a map of ids:uuids for everything in the project
|
|
1104
|
+
*/
|
|
1105
|
+
getUUIDMap(options = {}) {
|
|
1106
|
+
const result = {};
|
|
1107
|
+
for (const wf of this.workflows) {
|
|
1108
|
+
result[wf.id] = {
|
|
1109
|
+
self: wf.openfn?.uuid,
|
|
1110
|
+
children: wf.getUUIDMap()
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
return result;
|
|
1114
|
+
}
|
|
1055
1115
|
};
|
|
1056
1116
|
var Project_default = Project;
|
|
1057
1117
|
|
|
@@ -1082,16 +1142,19 @@ var Workspace = class {
|
|
|
1082
1142
|
projectPaths = /* @__PURE__ */ new Map();
|
|
1083
1143
|
isValid = false;
|
|
1084
1144
|
constructor(workspacePath) {
|
|
1085
|
-
const projectsPath = path2.join(workspacePath, PROJECTS_DIRECTORY);
|
|
1086
1145
|
const openfnYamlPath = path2.join(workspacePath, OPENFN_YAML_FILE);
|
|
1087
1146
|
if (pathExists(openfnYamlPath, "file")) {
|
|
1088
1147
|
this.isValid = true;
|
|
1089
1148
|
const data = fs3.readFileSync(openfnYamlPath, "utf-8");
|
|
1090
1149
|
this.config = yamlToJson(data);
|
|
1091
1150
|
}
|
|
1151
|
+
const projectsPath = path2.join(
|
|
1152
|
+
workspacePath,
|
|
1153
|
+
this.config?.dirs?.projects ?? PROJECTS_DIRECTORY
|
|
1154
|
+
);
|
|
1092
1155
|
if (this.isValid && pathExists(projectsPath, "directory")) {
|
|
1093
1156
|
const stateFiles = fs3.readdirSync(projectsPath).filter(
|
|
1094
|
-
(fileName) => PROJECT_EXTENSIONS.includes(path2.extname(fileName))
|
|
1157
|
+
(fileName) => PROJECT_EXTENSIONS.includes(path2.extname(fileName)) && path2.parse(fileName).name !== "openfn"
|
|
1095
1158
|
);
|
|
1096
1159
|
this.projects = stateFiles.map((file) => {
|
|
1097
1160
|
const stateFilePath = path2.join(projectsPath, file);
|
|
@@ -1099,7 +1162,7 @@ var Workspace = class {
|
|
|
1099
1162
|
const project = from_app_state_default(data, { format: "yaml" });
|
|
1100
1163
|
this.projectPaths.set(project.name, stateFilePath);
|
|
1101
1164
|
return project;
|
|
1102
|
-
});
|
|
1165
|
+
}).filter((s) => s);
|
|
1103
1166
|
}
|
|
1104
1167
|
}
|
|
1105
1168
|
list() {
|
|
@@ -1133,16 +1196,21 @@ import { grammar } from "ohm-js";
|
|
|
1133
1196
|
var parser;
|
|
1134
1197
|
var initOperations = (options = {}) => {
|
|
1135
1198
|
let nodes = {};
|
|
1136
|
-
const
|
|
1199
|
+
const uuidMap = options.uuidMap ?? {};
|
|
1200
|
+
const uuid = (id) => {
|
|
1201
|
+
if (id in uuidMap) {
|
|
1202
|
+
return uuidMap[id];
|
|
1203
|
+
}
|
|
1137
1204
|
return options.uuidSeed ? options.uuidSeed++ : randomUUID2();
|
|
1138
1205
|
};
|
|
1139
1206
|
const buildNode = (name) => {
|
|
1140
1207
|
if (!nodes[name]) {
|
|
1208
|
+
const id = slugify(name);
|
|
1141
1209
|
nodes[name] = {
|
|
1142
1210
|
name,
|
|
1143
|
-
id
|
|
1211
|
+
id,
|
|
1144
1212
|
openfn: {
|
|
1145
|
-
uuid: uuid()
|
|
1213
|
+
uuid: uuid(id)
|
|
1146
1214
|
}
|
|
1147
1215
|
};
|
|
1148
1216
|
}
|
|
@@ -1169,6 +1237,7 @@ var initOperations = (options = {}) => {
|
|
|
1169
1237
|
const n1 = parent.buildWorkflow();
|
|
1170
1238
|
const n2 = child.buildWorkflow();
|
|
1171
1239
|
const e = edge.buildWorkflow();
|
|
1240
|
+
e.openfn.uuid = uuid(`${n1.id}-${n2.id}`);
|
|
1172
1241
|
n1.next ??= {};
|
|
1173
1242
|
n1.next[n2.name] = e;
|
|
1174
1243
|
return [n1, n2];
|
|
@@ -1197,9 +1266,6 @@ var initOperations = (options = {}) => {
|
|
|
1197
1266
|
return props.asIteration().children.map((c) => c.buildWorkflow());
|
|
1198
1267
|
},
|
|
1199
1268
|
prop(key, _op, value) {
|
|
1200
|
-
if (value._iter) {
|
|
1201
|
-
console.log(">>>> ITER");
|
|
1202
|
-
}
|
|
1203
1269
|
return [key.sourceString, value.buildWorkflow()];
|
|
1204
1270
|
},
|
|
1205
1271
|
// Bit flaky - we need this to handle quoted props
|
|
@@ -1214,16 +1280,15 @@ var initOperations = (options = {}) => {
|
|
|
1214
1280
|
},
|
|
1215
1281
|
edge(_) {
|
|
1216
1282
|
return {
|
|
1217
|
-
openfn: {
|
|
1218
|
-
uuid: uuid()
|
|
1219
|
-
}
|
|
1283
|
+
openfn: {}
|
|
1220
1284
|
};
|
|
1221
1285
|
}
|
|
1222
1286
|
};
|
|
1223
1287
|
return operations;
|
|
1224
1288
|
};
|
|
1225
1289
|
var createParser = () => {
|
|
1226
|
-
const
|
|
1290
|
+
const grammarPath = path3.resolve(import.meta.dirname, "workflow.ohm");
|
|
1291
|
+
const contents = readFileSync(grammarPath, "utf-8");
|
|
1227
1292
|
const parser2 = grammar(contents);
|
|
1228
1293
|
return {
|
|
1229
1294
|
parse(str, options) {
|
|
@@ -1247,19 +1312,31 @@ function generateWorkflow(def, options = {}) {
|
|
|
1247
1312
|
if (!parser) {
|
|
1248
1313
|
parser = createParser();
|
|
1249
1314
|
}
|
|
1250
|
-
const
|
|
1315
|
+
const raw = parser.parse(def, options);
|
|
1316
|
+
if (!raw.name) {
|
|
1317
|
+
raw.name = "Workflow";
|
|
1318
|
+
}
|
|
1319
|
+
if (!raw.id) {
|
|
1320
|
+
raw.id = "workflow";
|
|
1321
|
+
}
|
|
1251
1322
|
if (options.openfnUuid) {
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
};
|
|
1323
|
+
raw.openfn ??= {};
|
|
1324
|
+
raw.openfn.uuid = randomUUID2();
|
|
1255
1325
|
}
|
|
1326
|
+
const wf = new Workflow_default(raw);
|
|
1256
1327
|
return wf;
|
|
1257
1328
|
}
|
|
1258
|
-
function generateProject(name, workflowDefs, options) {
|
|
1259
|
-
const workflows = workflowDefs.map(
|
|
1329
|
+
function generateProject(name, workflowDefs, options = {}) {
|
|
1330
|
+
const workflows = workflowDefs.map(
|
|
1331
|
+
(w, idx) => generateWorkflow(w, {
|
|
1332
|
+
...options,
|
|
1333
|
+
uuidMap: options.uuidMap && options.uuidMap[idx]
|
|
1334
|
+
})
|
|
1335
|
+
);
|
|
1260
1336
|
return new Project_default({
|
|
1261
1337
|
name,
|
|
1262
|
-
workflows
|
|
1338
|
+
workflows,
|
|
1339
|
+
openfn: options.openfnUuid && { uuid: randomUUID2() }
|
|
1263
1340
|
});
|
|
1264
1341
|
}
|
|
1265
1342
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ohm grammar for interpreting our Workflow syntax
|
|
3
|
+
* https://ohmjs.org/docs/intro
|
|
4
|
+
*/
|
|
5
|
+
Workflow {
|
|
6
|
+
Workflow = attribute* Pair*
|
|
7
|
+
|
|
8
|
+
attribute = "@" attr_name space attr_name
|
|
9
|
+
|
|
10
|
+
attr_name = (alnum | "_" | "-")+
|
|
11
|
+
|
|
12
|
+
Pair = node edge node
|
|
13
|
+
|
|
14
|
+
comment = "#" (~lineTerminator any)*
|
|
15
|
+
|
|
16
|
+
space := " " | "\t" | lineTerminator | comment
|
|
17
|
+
|
|
18
|
+
/* lower case is important: it disables whitespace */
|
|
19
|
+
node = nodeWithProps | node_name
|
|
20
|
+
|
|
21
|
+
nodeWithProps = node_name props
|
|
22
|
+
|
|
23
|
+
/* A node name can contain letters, numbers or underscores */
|
|
24
|
+
node_name = (alnum | "_")+
|
|
25
|
+
|
|
26
|
+
props = "(" listOf<prop, ","> ")"
|
|
27
|
+
|
|
28
|
+
prop = alnum+ "=" propValue
|
|
29
|
+
|
|
30
|
+
propValue = alnum+ | quotedProp
|
|
31
|
+
|
|
32
|
+
quotedProp = "\"" (~"\"" any)* "\""
|
|
33
|
+
|
|
34
|
+
edge = "-"
|
|
35
|
+
|
|
36
|
+
lineTerminator = "\n" | "\r"
|
|
37
|
+
}
|
|
38
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openfn/project",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Read, serialize, replicate and sync OpenFn projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"lodash-es": "^4.17.21",
|
|
33
33
|
"ohm-js": "^17.2.1",
|
|
34
34
|
"yaml": "^2.2.2",
|
|
35
|
-
"@openfn/lexicon": "^1.2.
|
|
35
|
+
"@openfn/lexicon": "^1.2.4",
|
|
36
36
|
"@openfn/logger": "1.0.6"
|
|
37
37
|
},
|
|
38
38
|
"files": [
|
|
@@ -43,8 +43,7 @@
|
|
|
43
43
|
"test": "pnpm ava",
|
|
44
44
|
"test:watch": "pnpm ava -w",
|
|
45
45
|
"test:types": "pnpm tsc --noEmit --project tsconfig.json",
|
|
46
|
-
"build": "tsup --config ../../tsup.config.js src/index.ts",
|
|
47
|
-
"build:watch": "pnpm build --watch",
|
|
46
|
+
"build": "tsup --config ../../tsup.config.js src/index.ts && cp src/gen/workflow.ohm dist/workflow.ohm",
|
|
48
47
|
"pack": "pnpm pack --pack-destination ../../dist"
|
|
49
48
|
}
|
|
50
49
|
}
|