@openfn/project 0.9.0 → 0.9.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/dist/index.d.ts CHANGED
@@ -2,6 +2,17 @@ import * as l from '@openfn/lexicon';
2
2
  import l__default, { WorkspaceConfig, UUID } from '@openfn/lexicon';
3
3
  import { Provisioner } from '@openfn/lexicon/lightning';
4
4
 
5
+ type SerializedProject = Omit<Partial<l.Project>, 'workflows'> & {
6
+ version: number;
7
+ workflows: SerializedWorkflow[];
8
+ };
9
+ type SerializedWorkflow = {
10
+ id: string;
11
+ name: string;
12
+ steps: l.Step[];
13
+ openfn?: l.ProjectMeta;
14
+ };
15
+
5
16
  type WithMeta<T> = T & {
6
17
  openfn?: l.NodeMeta;
7
18
  };
@@ -89,6 +100,7 @@ declare class Project {
89
100
  config: l__default.WorkspaceConfig;
90
101
  collections: any;
91
102
  credentials: string[];
103
+ static from(type: 'project', data: any, options: never): Promise<Project>;
92
104
  static from(type: 'state', data: Provisioner.Project, meta?: Partial<l__default.ProjectMeta>, config?: fromAppStateConfig): Promise<Project>;
93
105
  static from(type: 'fs', options: FromFsConfig): Promise<Project>;
94
106
  static from(type: 'path', data: string, options?: {
@@ -97,7 +109,7 @@ declare class Project {
97
109
  static merge(source: Project, target: Project, options?: Partial<MergeProjectOptions>): Project;
98
110
  constructor(data: Partial<l__default.Project>, config?: Partial<l__default.WorkspaceConfig>);
99
111
  setConfig(config: Partial<WorkspaceConfig>): void;
100
- serialize(type?: 'json' | 'yaml' | 'fs' | 'state', options?: any): any;
112
+ serialize(type?: 'project' | 'fs' | 'state', options?: any): string | Record<string, string> | Provisioner.Project_v1 | SerializedProject;
101
113
  getWorkflow(idOrName: string): Workflow | undefined;
102
114
  getIdentifier(): string;
103
115
  getUUID(workflow: string | Workflow, stepId: string, otherStep?: string): any;
package/dist/index.js CHANGED
@@ -210,7 +210,7 @@ var Workflow = class {
210
210
  return this.index.uuid[id];
211
211
  }
212
212
  toJSON() {
213
- return this.workflow;
213
+ return clone(this.workflow);
214
214
  }
215
215
  getUUIDMap() {
216
216
  return this.index.uuid;
@@ -235,28 +235,10 @@ var Workflow_default = Workflow;
235
235
  var serialize_exports = {};
236
236
  __export(serialize_exports, {
237
237
  fs: () => to_fs_default,
238
- json: () => to_json_default,
238
+ project: () => to_project_default,
239
239
  state: () => to_app_state_default
240
240
  });
241
241
 
242
- // src/serialize/to-json.ts
243
- function to_json_default(project) {
244
- return {
245
- // There must be a better way to do this?
246
- // Do we just serialize all public fields?
247
- id: project.id,
248
- name: project.name,
249
- description: project.description,
250
- config: project.config,
251
- meta: project.meta,
252
- workflows: project.workflows.map((w) => w.toJSON()),
253
- collections: project.collections,
254
- credentials: project.credentials,
255
- openfn: project.openfn,
256
- options: project.options
257
- };
258
- }
259
-
260
242
  // src/serialize/to-app-state.ts
261
243
  import { pick, omitBy, isNil, sortBy } from "lodash-es";
262
244
  import { randomUUID } from "node:crypto";
@@ -291,7 +273,14 @@ var defaultJobProps = {
291
273
  project_credential_id: null
292
274
  };
293
275
  function to_app_state_default(project, options = {}) {
294
- const { uuid, endpoint, env, ...rest } = project.openfn ?? {};
276
+ const {
277
+ uuid,
278
+ endpoint,
279
+ env,
280
+ id,
281
+ fetched_at,
282
+ ...rest
283
+ } = project.openfn ?? {};
295
284
  const state = omitBy(
296
285
  pick(project, ["name", "description", "collections"]),
297
286
  isNil
@@ -382,6 +371,7 @@ var mapWorkflow = (workflow) => {
382
371
 
383
372
  // src/serialize/to-fs.ts
384
373
  import nodepath from "path";
374
+ import { omit } from "lodash-es";
385
375
 
386
376
  // src/util/config.ts
387
377
  import { readFileSync } from "node:fs";
@@ -508,10 +498,16 @@ var extractWorkflow = (project, workflowId) => {
508
498
  // Not crazy about this - maybe we should do something better? Or do we like the consistency?
509
499
  options: workflow.options,
510
500
  steps: workflow.steps.map((step) => {
511
- const { openfn, expression, ...mapped } = step;
501
+ const { openfn, expression, next, ...mapped } = step;
512
502
  if (expression) {
513
503
  mapped.expression = `./${step.id}.js`;
514
504
  }
505
+ if (next && typeof next === "object") {
506
+ mapped.next = {};
507
+ for (const id in next) {
508
+ mapped.next[id] = omit(next[id], ["openfn"]);
509
+ }
510
+ }
515
511
  return mapped;
516
512
  })
517
513
  };
@@ -546,18 +542,52 @@ var handleOutput = (data, filePath, format) => {
546
542
  return { path: path5, content };
547
543
  };
548
544
 
549
- // src/parse/from-app-state.ts
550
- var from_app_state_default = (state, meta = {}, config = {}) => {
551
- let stateJson;
552
- if (typeof state === "string") {
553
- if (config.format === "yaml") {
554
- stateJson = yamlToJson(state);
545
+ // src/serialize/to-project.ts
546
+ import { omitBy as omitBy2, isNil as isNil3 } from "lodash-es";
547
+ var SERIALIZE_VERSION = 2;
548
+ var to_project_default = (project, options = {}) => {
549
+ const proj = omitBy2(
550
+ {
551
+ id: project.id,
552
+ name: project.name,
553
+ version: SERIALIZE_VERSION,
554
+ // important!
555
+ description: project.description,
556
+ collections: project.collections,
557
+ credentials: project.credentials,
558
+ openfn: project.openfn,
559
+ meta: project.meta,
560
+ options: omitBy2(project.options, isNil3),
561
+ //workflows: project.workflows.map(mapWorkflow) as SerializedWorkflow[],
562
+ workflows: project.workflows.map(
563
+ (w) => w.toJSON()
564
+ )
565
+ },
566
+ isNil3
567
+ );
568
+ const format = options.format ?? proj.config?.formats.project;
569
+ if (format === "json") {
570
+ return proj;
571
+ }
572
+ return jsonToYaml(proj);
573
+ };
574
+
575
+ // src/util/ensure-json.ts
576
+ var ensure_json_default = (obj) => {
577
+ if (typeof obj === "string") {
578
+ const firstChar = obj.trim()[0];
579
+ if (firstChar === "{" || firstChar === "[") {
580
+ return JSON.parse(obj);
555
581
  } else {
556
- stateJson = JSON.parse(state);
582
+ return yamlToJson(obj);
557
583
  }
558
- } else {
559
- stateJson = state;
560
584
  }
585
+ return obj;
586
+ };
587
+
588
+ // src/parse/from-app-state.ts
589
+ var from_app_state_default = (state, meta = {}, config = {}) => {
590
+ let stateJson = ensure_json_default(state);
561
591
  delete config.format;
562
592
  const {
563
593
  id,
@@ -660,24 +690,32 @@ var mapWorkflow2 = (workflow) => {
660
690
  };
661
691
 
662
692
  // src/parse/from-path.ts
663
- import { extname } from "node:path";
664
693
  import { readFile } from "node:fs/promises";
665
- import { omit } from "lodash-es";
666
- var from_path_default = async (path5, config = {}) => {
667
- const ext = extname(path5).toLowerCase();
668
- const source = await readFile(path5, "utf8");
669
- let state;
670
- if (ext === ".json") {
671
- config.format = "json";
672
- state = JSON.parse(source);
673
- } else if (ext.match(/(ya?ml)$/)) {
674
- config.format = "yaml";
675
- state = yamlToJson(source);
694
+
695
+ // src/parse/from-project.ts
696
+ var from_project_default = (data, config) => {
697
+ let rawJson = ensure_json_default(data);
698
+ let json;
699
+ if (rawJson.version) {
700
+ json = from_v2(rawJson);
676
701
  } else {
677
- throw new Error(`Cannot load a project from a ${ext} file`);
702
+ json = from_v1(rawJson);
678
703
  }
679
- const meta = {};
680
- return from_app_state_default(state, meta, omit(config, ["format"]));
704
+ return new Project_default(json, config);
705
+ };
706
+ var from_v1 = (data) => {
707
+ return from_app_state_default(data);
708
+ };
709
+ var from_v2 = (data) => {
710
+ return {
711
+ ...data
712
+ };
713
+ };
714
+
715
+ // src/parse/from-path.ts
716
+ var from_path_default = async (path5, config = {}) => {
717
+ const source = await readFile(path5, "utf8");
718
+ return from_project_default(source, config);
681
719
  };
682
720
 
683
721
  // src/parse/from-fs.ts
@@ -699,6 +737,7 @@ var get_identifier_default = (config = {}) => {
699
737
  };
700
738
 
701
739
  // src/parse/from-fs.ts
740
+ import { omit as omit2 } from "lodash-es";
702
741
  var parseProject = async (options) => {
703
742
  const { root } = options;
704
743
  const { type, content } = findWorkspaceFile(root);
@@ -717,13 +756,13 @@ var parseProject = async (options) => {
717
756
  `${identifier}.${format}`
718
757
  );
719
758
  const stateFile = await fs.readFile(statePath, "utf8");
720
- state = from_app_state_default(stateFile, { format });
759
+ state = from_project_default(stateFile, config);
721
760
  } catch (e) {
722
761
  console.warn(`Failed to find state file for ${identifier}`);
723
762
  }
724
763
  const proj = {
725
764
  name: state?.name,
726
- openfn: context.project,
765
+ openfn: omit2(context.project, ["id"]),
727
766
  config,
728
767
  workflows: []
729
768
  };
@@ -1210,6 +1249,7 @@ var Project = class {
1210
1249
  options;
1211
1250
  // local metadata used by the CLI
1212
1251
  // This stuff is not synced back to lightning
1252
+ // TODO maybe rename cli or local
1213
1253
  meta;
1214
1254
  // this contains meta about the connected openfn project
1215
1255
  openfn;
@@ -1218,17 +1258,23 @@ var Project = class {
1218
1258
  collections;
1219
1259
  credentials;
1220
1260
  static async from(type, data, ...rest) {
1221
- if (type === "state") {
1222
- return from_app_state_default(data, rest[0], rest[1]);
1223
- } else if (type === "fs") {
1224
- return parseProject(data);
1225
- } else if (type === "path") {
1226
- return from_path_default(data, rest[0]);
1261
+ switch (type) {
1262
+ case "project":
1263
+ var [config] = rest;
1264
+ return from_project_default(data, config);
1265
+ case "state":
1266
+ return from_app_state_default(data, rest[0], rest[1]);
1267
+ case "fs":
1268
+ return parseProject(data);
1269
+ case "path":
1270
+ var [config] = rest;
1271
+ return from_path_default(data, config);
1272
+ default:
1273
+ throw new Error(`Didn't recognize type ${type}`);
1227
1274
  }
1228
- throw new Error(`Didn't recognize type ${type}`);
1229
1275
  }
1230
1276
  // Diff two projects
1231
- // /static diff(a: Project, b: Project) {}
1277
+ // static diff(a: Project, b: Project) {}
1232
1278
  // Merge a source project (staging) into the target project (main)
1233
1279
  // Returns a new Project
1234
1280
  // TODO: throw if histories have diverged
@@ -1254,7 +1300,7 @@ var Project = class {
1254
1300
  setConfig(config) {
1255
1301
  this.config = buildConfig(config);
1256
1302
  }
1257
- serialize(type = "json", options) {
1303
+ serialize(type = "project", options) {
1258
1304
  if (type in serialize_exports) {
1259
1305
  return serialize_exports[type](this, options);
1260
1306
  }
@@ -1317,7 +1363,7 @@ function pathExists(fpath, type) {
1317
1363
 
1318
1364
  // src/Workspace.ts
1319
1365
  var Workspace = class {
1320
- // @ts-ignore config not defininitely assigned - it sure is
1366
+ // @ts-ignore config not definitely assigned - it sure is
1321
1367
  config;
1322
1368
  activeProject;
1323
1369
  projects = [];
@@ -1345,14 +1391,9 @@ var Workspace = class {
1345
1391
  const stateFilePath = path3.join(projectsPath, file);
1346
1392
  try {
1347
1393
  const data = fs3.readFileSync(stateFilePath, "utf-8");
1348
- const project = from_app_state_default(
1349
- data,
1350
- {},
1351
- {
1352
- ...this.config,
1353
- format: this.config?.formats.project
1354
- }
1355
- );
1394
+ const project = from_project_default(data, {
1395
+ ...this.config
1396
+ });
1356
1397
  this.projectPaths.set(project.id, stateFilePath);
1357
1398
  return project;
1358
1399
  } catch (e) {
@@ -1400,7 +1441,7 @@ import { randomUUID as randomUUID2 } from "node:crypto";
1400
1441
  import path4 from "node:path";
1401
1442
  import { readFileSync as readFileSync2 } from "node:fs";
1402
1443
  import { grammar } from "ohm-js";
1403
- import { isNil as isNil3, set } from "lodash-es";
1444
+ import { isNil as isNil4, set } from "lodash-es";
1404
1445
  var parser;
1405
1446
  var expectedNodeProps = [
1406
1447
  // TODO need to clarify adaptor/adaptors confusion
@@ -1571,7 +1612,7 @@ function generateWorkflow(def, options = {}) {
1571
1612
  if (options.uuidMap && raw.id in options.uuidMap) {
1572
1613
  uuid = options.uuidMap[raw.id];
1573
1614
  }
1574
- if (!isNil3(uuid) && options.openfnUuid) {
1615
+ if (!isNil4(uuid) && options.openfnUuid) {
1575
1616
  raw.openfn ??= {};
1576
1617
  raw.openfn.uuid = uuid;
1577
1618
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfn/project",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Read, serialize, replicate and sync OpenFn projects",
5
5
  "type": "module",
6
6
  "exports": {
@@ -34,8 +34,8 @@
34
34
  "lodash-es": "^4.17.21",
35
35
  "ohm-js": "^17.2.1",
36
36
  "yaml": "^2.2.2",
37
- "@openfn/lexicon": "^1.2.6",
38
- "@openfn/logger": "1.0.6"
37
+ "@openfn/lexicon": "^1.2.7",
38
+ "@openfn/logger": "1.1.0"
39
39
  },
40
40
  "files": [
41
41
  "dist",