@ingestron/core 0.12.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 (84) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +6 -0
  3. package/README.md +56 -0
  4. package/SECURITY.md +44 -0
  5. package/THIRD_PARTY_NOTICES.md +693 -0
  6. package/dist/assets/schemas/ODCS-LICENSE +201 -0
  7. package/dist/assets/schemas/odcs-3.1.0.json +2929 -0
  8. package/dist/assets/schemas/provenance.json +16 -0
  9. package/dist/compiler-fingerprint.json +84 -0
  10. package/dist/core/authoring.d.ts +55 -0
  11. package/dist/core/authoring.js +315 -0
  12. package/dist/core/config.d.ts +34 -0
  13. package/dist/core/config.js +233 -0
  14. package/dist/core/connections.d.ts +19 -0
  15. package/dist/core/connections.js +203 -0
  16. package/dist/core/connector-package.d.ts +37 -0
  17. package/dist/core/connector-package.js +43 -0
  18. package/dist/core/contracts.d.ts +8 -0
  19. package/dist/core/contracts.js +45 -0
  20. package/dist/core/deliveries.d.ts +16 -0
  21. package/dist/core/deliveries.js +28 -0
  22. package/dist/core/delivery-exports.d.ts +20 -0
  23. package/dist/core/delivery-exports.js +119 -0
  24. package/dist/core/development.d.ts +50 -0
  25. package/dist/core/development.js +94 -0
  26. package/dist/core/ecosystem-authoring.d.ts +13 -0
  27. package/dist/core/ecosystem-authoring.js +82 -0
  28. package/dist/core/errors.d.ts +12 -0
  29. package/dist/core/errors.js +43 -0
  30. package/dist/core/execution.d.ts +28 -0
  31. package/dist/core/execution.js +297 -0
  32. package/dist/core/extension-packs.d.ts +8 -0
  33. package/dist/core/extension-packs.js +34 -0
  34. package/dist/core/fingerprint.d.ts +1 -0
  35. package/dist/core/fingerprint.js +16 -0
  36. package/dist/core/generate.d.ts +16 -0
  37. package/dist/core/generate.js +198 -0
  38. package/dist/core/installation.d.ts +3 -0
  39. package/dist/core/installation.js +14 -0
  40. package/dist/core/installed-plugins.d.ts +41 -0
  41. package/dist/core/installed-plugins.js +92 -0
  42. package/dist/core/model-pack-schema.d.ts +46 -0
  43. package/dist/core/model-pack-schema.js +81 -0
  44. package/dist/core/model-packs.d.ts +4 -0
  45. package/dist/core/model-packs.js +33 -0
  46. package/dist/core/operations.d.ts +346 -0
  47. package/dist/core/operations.js +760 -0
  48. package/dist/core/package-references.d.ts +36 -0
  49. package/dist/core/package-references.js +103 -0
  50. package/dist/core/packages.d.ts +340 -0
  51. package/dist/core/packages.js +579 -0
  52. package/dist/core/planner.d.ts +114 -0
  53. package/dist/core/planner.js +821 -0
  54. package/dist/core/project-build.d.ts +13 -0
  55. package/dist/core/project-build.js +266 -0
  56. package/dist/core/provider-commands.d.ts +83 -0
  57. package/dist/core/provider-commands.js +252 -0
  58. package/dist/core/provider-starter.d.ts +5 -0
  59. package/dist/core/provider-starter.js +155 -0
  60. package/dist/core/report-pack-schema.d.ts +13 -0
  61. package/dist/core/report-pack-schema.js +15 -0
  62. package/dist/core/schema.d.ts +257 -0
  63. package/dist/core/schema.js +126 -0
  64. package/dist/core/workflows.d.ts +91 -0
  65. package/dist/core/workflows.js +438 -0
  66. package/dist/plugins/authoring.d.ts +88 -0
  67. package/dist/plugins/authoring.js +34 -0
  68. package/dist/plugins/compatibility.d.ts +23 -0
  69. package/dist/plugins/compatibility.js +55 -0
  70. package/dist/plugins/generation.d.ts +5 -0
  71. package/dist/plugins/generation.js +105 -0
  72. package/dist/plugins/provider-renderer.d.ts +7 -0
  73. package/dist/plugins/provider-renderer.js +37 -0
  74. package/dist/plugins/provider-worker.d.ts +1 -0
  75. package/dist/plugins/provider-worker.js +74 -0
  76. package/dist/sdk/adapter.d.ts +15 -0
  77. package/dist/sdk/adapter.js +15 -0
  78. package/dist/sdk/index.d.ts +4 -0
  79. package/dist/sdk/index.js +3 -0
  80. package/dist/sdk/schemas.d.ts +34 -0
  81. package/dist/sdk/schemas.js +74 -0
  82. package/dist/version.d.ts +5 -0
  83. package/dist/version.js +7 -0
  84. package/package.json +72 -0
@@ -0,0 +1,16 @@
1
+ [
2
+ {
3
+ "file": "odcs-3.1.0.json",
4
+ "source": "https://api.github.com/repos/bitol-io/open-data-contract-standard/git/blobs/fb74095224e367b6be7bb12d96a3624db2685c81",
5
+ "blob": "fb74095224e367b6be7bb12d96a3624db2685c81",
6
+ "sha256": "1a35de14c688b400fef306564f66e2af3304f057cd5cd6e7b847c1a8bf252c5b",
7
+ "accessed": "2026-09-06"
8
+ },
9
+ {
10
+ "file": "ODCS-LICENSE",
11
+ "source": "https://api.github.com/repos/bitol-io/open-data-contract-standard/git/blobs/261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64",
12
+ "blob": "261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64",
13
+ "sha256": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4",
14
+ "accessed": "2026-09-06"
15
+ }
16
+ ]
@@ -0,0 +1,84 @@
1
+ {
2
+ "files": {
3
+ "dist/assets/schemas/ODCS-LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4",
4
+ "dist/assets/schemas/odcs-3.1.0.json": "1a35de14c688b400fef306564f66e2af3304f057cd5cd6e7b847c1a8bf252c5b",
5
+ "dist/assets/schemas/provenance.json": "4b5eb1fd89b81223f7946113b651d5a3a18adaa28948e43c3615dd851fb6844e",
6
+ "dist/core/authoring.js": "43025703f23b61090d1ea6912e387e5ed74a93568f068339f9a7ecae88795a51",
7
+ "dist/core/config.js": "b7e7138f7cc4a7b4ec8f6d0f0616c8802f2d802ec29aa09b40c1fc5c9324e016",
8
+ "dist/core/connections.js": "bd351bcb05275321078409b4de83648751a51e609ab5c5fc5489f1fe31433bcd",
9
+ "dist/core/connector-package.js": "c1d39fce629a524fa199ed5d0dffe6a36c500e1018fbb8379d700330372b46a7",
10
+ "dist/core/contracts.js": "a1b7fd28f776117cca86a25c6c70e852ff57ffea2bb9e7accbf2d583948d9dde",
11
+ "dist/core/deliveries.js": "a4d2cb1213bc4c02cffb962dcb943ba98e261d37cfb21041948ec637599db080",
12
+ "dist/core/delivery-exports.js": "d895b044c08f2059f0275724205120c72dd4bf5b74170bb01bdf42dd9d526d56",
13
+ "dist/core/development.js": "2b55b15d545172e527cf66868d96d97c5ca081d0a83892ffd253eadaa3cc1b39",
14
+ "dist/core/ecosystem-authoring.js": "782f20a08408af1c3c93ab27b521a86f2d6cddac042c2503cb58c4a76fdcd6fc",
15
+ "dist/core/errors.js": "8117e35fba84ae34627cb7d3bef46aaf6d8718c512140fa0ab08ccbdeeeec8c3",
16
+ "dist/core/execution.js": "496807e7428528e31e76d51144dfd84682d435467b30a35574310af525ddeca7",
17
+ "dist/core/extension-packs.js": "6b2aaa83e51f40e176cc4cfed574a4a17646bb3f526666fe8e0456d2912a8045",
18
+ "dist/core/fingerprint.js": "0edb56b544171ac20cc97fd58850eda5acefd3c725114b9d8bcc15b4971c5a36",
19
+ "dist/core/generate.js": "7f80617867186d216d49fcd91ebefb1807d7fe22c62b3b95ae311fe5096b08e8",
20
+ "dist/core/installation.js": "1eacb76525839743a2ebda9c24dc0a96edf485113fd4892e799663c60c31b548",
21
+ "dist/core/installed-plugins.js": "817ed411aef6f5b966a1828893081406805d2c8764d2ca20fd7e250e8a2168a8",
22
+ "dist/core/model-pack-schema.js": "e3eb7ff33ddc652643e3f896637de0034004cd0cf26b0e296a074f830ae0b870",
23
+ "dist/core/model-packs.js": "fee776ea0804dc66c9f140e57d94a3922d69c52d03c72079842ec43417616b0d",
24
+ "dist/core/operations.js": "bd6217f7eb33c3c5649f8edecab3ce87d4774c9ff790feff8763ebab71866531",
25
+ "dist/core/package-references.js": "bd3085eb6dcdac399d79af109d0a9e710a0532eba61e5298dc842aec9c7ef10a",
26
+ "dist/core/packages.js": "9c699b518a5c364c361d2b856f3fc6e67fe43ce0dc906b8aa474e341c5e49110",
27
+ "dist/core/planner.js": "f91902f4c37a7588965360fcddbda79301b59a0b66ef2f70c0378fa5dfcc7088",
28
+ "dist/core/project-build.js": "1f1002b4f6c506986c5aa6a5bb01ad5f3b4658a7a1319b65d79eaa67916fb994",
29
+ "dist/core/provider-commands.js": "8bd82821f6ed0eefce8216e7c03c271f28cb9f4bfda7724d5c1de6be1855e535",
30
+ "dist/core/provider-starter.js": "0ad05cbfed3d934196f82f8b50cb982314796ef9c5f012ad49935166e93e0fb0",
31
+ "dist/core/report-pack-schema.js": "fe8ecdc2b8deacd8b2a1e46588ee06744a7b6d8e7342786e320f74cc848665fa",
32
+ "dist/core/schema.js": "4cbad607f372584fe83f44a121bd978dea7529db5c1eb51168dcd62812c2eeab",
33
+ "dist/core/workflows.js": "1a5fe93b34d963efb07ab1f67641106386fa3ef9d9e80bd3e59c13795925fda6",
34
+ "dist/plugins/authoring.js": "ff985a7173c7aeda4a431a8603a4ab8631dc5134486578ede7d1301ebc3e6a19",
35
+ "dist/plugins/compatibility.js": "a6591cfa4ca5a324fd29856e51319e39be470d6ed835ee268b293727ce11f8b7",
36
+ "dist/plugins/generation.js": "7b0bae4de23edab4c2596d1f8ed2244d70b30fcae60395994b3964ffc845e9d5",
37
+ "dist/plugins/provider-renderer.js": "b05c469e19cfd57a47fb81e2dcf7fc01f27fcd09bdf2e6f5679a39e99365753f",
38
+ "dist/plugins/provider-worker.js": "ad66928002e95eefd0026c7d246e12e0b32f67e26f97164190f2c91518142602",
39
+ "dist/sdk/adapter.js": "d613c1ec890ce1a7384890a97cb50d3e272d34631a67c65d90349a227267df44",
40
+ "dist/sdk/index.js": "6d136aaf90903fbcc2de4b40fbed9f361472161c60fc5b1b8a9b9e5583ceefdd",
41
+ "dist/sdk/schemas.js": "7df73031cd262b2130e16861415873cd55086353f8b4b2a648a6910907e9f56e",
42
+ "dist/version.js": "48af2aa1e425f1e387c082bbf0ad2c0c8d757c468101a614c3864153a94e6889"
43
+ },
44
+ "sources": {
45
+ "src/core/authoring.ts": "fe80e823cfb82b39894aac9c5f733d489241b4a22bed278079254acff45a0aca",
46
+ "src/core/config.ts": "f282eb246438608e1951556dfeab9372daf65330a15a9d159d36d1f940370344",
47
+ "src/core/connections.ts": "19327f428382189a73a462cfd0b48943c0ba3b000633412b35da1d2ded81868b",
48
+ "src/core/connector-package.ts": "f302432c3954a6230ddf02e3ae796c0a641e52984c0db3bfbfc10b3d5539e49e",
49
+ "src/core/contracts.ts": "b7445086085b17d5ae8d5a7f4e2e849f286948e1a2a146ceb79660846e8616e5",
50
+ "src/core/deliveries.ts": "6be0a7059c057420ba22a5515703164a892fac9e79a5bdb0e0871ea0b92ddd15",
51
+ "src/core/delivery-exports.ts": "45a4d13e73a3809340fcd0f018dd06e2e360c67c1a117c672cd48be463c82d58",
52
+ "src/core/development.ts": "decd47d8f6ca3289fea45331f27b99b0feb7188ae0a9e152c6459cc00658e7bd",
53
+ "src/core/ecosystem-authoring.ts": "4c4385f2c9a3208d32e425183555916742a856dc6cc1bb44a806e94bb7537cba",
54
+ "src/core/errors.ts": "55e7ec3239bb3bcae73cf37e700cd83b9ea3336bf5514062827a3627a606977d",
55
+ "src/core/execution.ts": "44ebbe7b6f64e0c3c224dd6f419939e500de92b7e4e8b283b0b33266e1d1fbf1",
56
+ "src/core/extension-packs.ts": "76671a6971598212c9ec0bb0ce9b75feee13dec09565fa0adc9f050a6ae668ec",
57
+ "src/core/fingerprint.ts": "f74ebb99800a07975ec16618fb5c70d2a7d33ab3d4bb6de37c4a8ba211e9847e",
58
+ "src/core/generate.ts": "5efadcd0957f124f44fa1af082d8850da24956e6f802de2fea43b6cd4460ecd2",
59
+ "src/core/installation.ts": "fefd909eb9583cb6c51fad7de421cfcd1da86969bdc1cf47ba1cf83a70ca23f0",
60
+ "src/core/installed-plugins.ts": "951d15cdd8ca4a2c7d97fb01a46838822fe1b2fcf083ff8cf4bdca20d0997a2d",
61
+ "src/core/model-pack-schema.ts": "bc9b30ab257fd25766cba63aa2fb6891c05e4b3eccf56d820caaab2dcc4147b4",
62
+ "src/core/model-packs.ts": "a110d319f6feeff22a2d0d3a99ce5e49e56817d0cc27ab77d831d04a945ddfd3",
63
+ "src/core/operations.ts": "3f63b1a7bab027c81a8b27dc31094910df34af6ea333b5f12590b9cf85904cb4",
64
+ "src/core/package-references.ts": "5c5cee7e140158928e2e5a37f426520c7c2f042546a37eed4507d79326181f5a",
65
+ "src/core/packages.ts": "b32d52adf6d4c3673cc70b5bc8638e5986fe4790b9e9ac386e057561cec13b32",
66
+ "src/core/planner.ts": "498f5405f3088ee6d54ec8ab4a458813757697c150a56eab33f547aebb214da1",
67
+ "src/core/project-build.ts": "feb0fe50aff7b1fbdc8d96bcdb177a72a412639d1af98c3cff3b010de90a6428",
68
+ "src/core/provider-commands.ts": "f8a045230f61be0273910276b51e5c2e4bc0659f34c8a9361a38ab292c39eb4c",
69
+ "src/core/provider-starter.ts": "e0f50e2de10d7ec345f64ab3f7b0a08bd0e7df70f36b7d9ccaed78d98ce3a47e",
70
+ "src/core/report-pack-schema.ts": "cf2c68f3725cef847627ce61acd0887f04d0067bc324841d8a552bcb12d274b0",
71
+ "src/core/schema.ts": "dc1c5464878916bbdee08662320310bb202a21ebfca9e534a30746603c9aa46f",
72
+ "src/core/workflows.ts": "b8afae8637eb92b58ab41a41fc245950e1c8e8820a6a27358508ae54c04c7a78",
73
+ "src/plugins/authoring.ts": "a0d68d406b4569c0b48eec458407d24c3d4eeed5b7918c6c4fcb3828ada93fe5",
74
+ "src/plugins/compatibility.ts": "b5d357a1dc2a972cf8209311602f9fd816ba0a59cbe747f86f1b5567754073ea",
75
+ "src/plugins/generation.ts": "a495906af09cf7bf820c7aba390bf742d7e94328ce708a2ff40f613f451fe237",
76
+ "src/plugins/provider-renderer.ts": "ed7b498fd2b0e0a0111f428dcbeeb9561397dcd0f4a661a4de352ef469d84d3f",
77
+ "src/plugins/provider-worker.ts": "ce6b7b3dfc60e3ddd8f281e7f55eb6b46402be31429aacfd3d298bff4fce7a48",
78
+ "src/sdk/adapter.ts": "f3a7617498763a245b5d6f68441b6af9a8c1ee6ae3f9ced5ff641c0078436be2",
79
+ "src/sdk/index.ts": "60bcd61da2a15404024b7d7489774e95c5d69ed58c93ce1133cc5d6f6db27c93",
80
+ "src/sdk/schemas.ts": "8d40480c31bc14ee3e9e0a2d0646e6bdc136c790647826f87568cde5475e33bf",
81
+ "scripts/build-assets.mjs": "faa89a8b2e9eedbd02d0582c0cb6cc35c4b7b79d9b4940bb2e31ae739baea52e",
82
+ "src/version.ts": "c1d73f25230389b6f911e973feb1fd9b142cb6b34626cecad25820b5953df4a4"
83
+ }
84
+ }
@@ -0,0 +1,55 @@
1
+ export interface Change {
2
+ path: string;
3
+ before: string | null;
4
+ after: string;
5
+ }
6
+ export interface Proposal {
7
+ apiVersion: "ingestron.change/v1";
8
+ id: string;
9
+ revision: string;
10
+ changes: Change[];
11
+ }
12
+ export declare function preview(root: string, replacements: Record<string, string>): Proposal;
13
+ export declare function apply(root: string, proposal: Proposal): {
14
+ applied: boolean;
15
+ proposalId: string;
16
+ files: string[];
17
+ };
18
+ export declare function initialise(root: string, options: {
19
+ id: string;
20
+ provider?: string;
21
+ environments?: string[];
22
+ }): Proposal;
23
+ export declare function migrateProvider(root: string, options: {
24
+ package: string;
25
+ to: string;
26
+ }): Proposal;
27
+ export declare function addFlow(root: string, options: {
28
+ id: string;
29
+ kind: "ingestion" | "transformation";
30
+ standard?: string;
31
+ sourceKind?: string;
32
+ format?: string;
33
+ sourceBinding?: string;
34
+ inputDataset?: string;
35
+ provider?: string;
36
+ }): Proposal;
37
+ export declare function addTable(root: string, options: {
38
+ flow: string;
39
+ id: string;
40
+ contract: string;
41
+ }): Proposal;
42
+ export declare function configureTable(root: string, flow: string, id: string, patch: Record<string, unknown>): Proposal;
43
+ export declare function addDataset(root: string, options: {
44
+ flow: string;
45
+ id: string;
46
+ contract: string;
47
+ from: string;
48
+ }): Proposal;
49
+ export declare function addEnvironment(root: string, options: {
50
+ name: string;
51
+ from: string;
52
+ }): Proposal;
53
+ export declare function editValue(root: string, environment: string, pointer: string, value: unknown): Proposal;
54
+ export declare function configureFlow(root: string, flow: string, patch: Record<string, unknown>): Proposal;
55
+ export declare function addStep(root: string, flow: string, step: unknown, after?: string): Proposal;
@@ -0,0 +1,315 @@
1
+ import { friendlyReference } from "./package-references.js";
2
+ import { providerAuthor, providerReference } from "../plugins/authoring.js";
3
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync, rmSync, realpathSync, } from "node:fs";
4
+ import { resolve, dirname, relative } from "node:path";
5
+ import { parseDocument, stringify } from "yaml";
6
+ import { check, digest, canonical, merge, Problem } from "./errors.js";
7
+ import { Configuration } from "./config.js";
8
+ import { contractColumns } from "./contracts.js";
9
+ import { fence, installedProviders, canonicalPackageReference, } from "./packages.js";
10
+ const yaml = (text, file) => {
11
+ const doc = parseDocument(text, { uniqueKeys: true });
12
+ check(!doc.errors.length, "YAML", doc.errors[0]?.message ?? "Invalid YAML", file);
13
+ return doc;
14
+ };
15
+ function revision(root) {
16
+ const lockFile = resolve(root, "packages.lock.yaml");
17
+ const lock = existsSync(lockFile) ? digest(readFileSync(lockFile)) : null;
18
+ if (!existsSync(resolve(root, "project.yaml")))
19
+ return digest(canonical({ project: null, lock }));
20
+ const reader = new Configuration(root);
21
+ reader.load();
22
+ return digest(canonical({ files: reader.files, lock }));
23
+ }
24
+ export function preview(root, replacements) {
25
+ root = resolve(root);
26
+ const changes = Object.entries(replacements)
27
+ .map(([name, after]) => {
28
+ const file = fence(root, name);
29
+ const before = existsSync(file) ? readFileSync(file, "utf8") : null;
30
+ return { path: name, before, after };
31
+ })
32
+ .filter((c) => c.before !== c.after);
33
+ const body = {
34
+ apiVersion: "ingestron.change/v1",
35
+ revision: revision(root),
36
+ changes,
37
+ };
38
+ return { ...body, id: digest(canonical(body)) };
39
+ }
40
+ export function apply(root, proposal) {
41
+ root = resolve(root);
42
+ const { id, ...body } = proposal;
43
+ check(id === digest(canonical(body)) &&
44
+ proposal.apiVersion === "ingestron.change/v1", "PROPOSAL", "Invalid change proposal");
45
+ check(revision(root) === proposal.revision, "STALE", "Project inputs changed since this proposal was reviewed");
46
+ check(Array.isArray(proposal.changes) && proposal.changes.length <= 1000, "PROPOSAL", "Invalid or oversized change list");
47
+ for (const change of proposal.changes)
48
+ check((change.path.split("/").at(-1) === ".gitignore" &&
49
+ !change.path
50
+ .split("/")
51
+ .slice(0, -1)
52
+ .some((part) => part.startsWith("."))) ||
53
+ (/\.(ya?ml|json|sql|py|m?js|md|txt|pbip|pbir|pbism|bim|svg|sql\.j2|py\.j2)$/.test(change.path) &&
54
+ !change.path.split("/").some((part) => part.startsWith("."))), "PATH", "Authoring applies only visible project configuration/source files");
55
+ for (const change of proposal.changes) {
56
+ const file = fence(root, change.path);
57
+ check((existsSync(file) ? readFileSync(file, "utf8") : null) === change.before, "STALE", `File changed since preview: ${change.path}`);
58
+ }
59
+ mkdirSync(root, { recursive: true });
60
+ const guard = fence(root, ".ingestron-write.lock");
61
+ try {
62
+ writeFileSync(guard, id, { flag: "wx" });
63
+ }
64
+ catch {
65
+ throw new Problem("CONFLICT", "Another authoring operation is active");
66
+ }
67
+ const written = [];
68
+ try {
69
+ check(revision(root) === proposal.revision, "STALE", "Project changed while waiting for write access");
70
+ for (const change of proposal.changes) {
71
+ const file = fence(root, change.path);
72
+ check((existsSync(file) ? readFileSync(file, "utf8") : null) ===
73
+ change.before, "STALE", `File changed: ${change.path}`);
74
+ }
75
+ for (const change of proposal.changes) {
76
+ const file = fence(root, change.path);
77
+ mkdirSync(dirname(file), { recursive: true });
78
+ const temp = file + `.edit-${process.pid}`;
79
+ writeFileSync(temp, change.after, { flag: "wx" });
80
+ try {
81
+ renameSync(temp, file);
82
+ written.push(change);
83
+ }
84
+ finally {
85
+ rmSync(temp, { force: true });
86
+ }
87
+ }
88
+ }
89
+ catch (error) {
90
+ for (const change of written.reverse()) {
91
+ const file = fence(root, change.path);
92
+ check(readFileSync(file, "utf8") === change.after, "CONFLICT", `Rollback stopped because ${change.path} changed externally`);
93
+ if (change.before === null)
94
+ rmSync(file);
95
+ else
96
+ writeFileSync(file, change.before);
97
+ }
98
+ throw error;
99
+ }
100
+ finally {
101
+ rmSync(guard, { force: true });
102
+ }
103
+ return { applied: true, proposalId: id, files: written.map((c) => c.path) };
104
+ }
105
+ export function initialise(root, options) {
106
+ check(/^[A-Za-z_][\w-]*$/.test(options.id), "PROJECT", "Invalid project ID");
107
+ const environments = options.environments ?? ["dev", "test", "prod"];
108
+ check(environments.length > 0 &&
109
+ new Set(environments).size === environments.length &&
110
+ environments.every((e) => /^[A-Za-z_][\w-]*$/.test(e)), "ENVIRONMENT", "Use distinct environment identifiers");
111
+ mkdirSync(root, { recursive: true });
112
+ if (!options.provider) {
113
+ const files = {
114
+ "project.yaml": stringify({
115
+ apiVersion: "ingestron.project/v1",
116
+ id: options.id,
117
+ providers: { packages: {}, configurations: {} },
118
+ defaults: {},
119
+ environments: Object.fromEntries(environments.map((name) => [
120
+ name,
121
+ { $resolve: `./environments/${name}.yaml` },
122
+ ])),
123
+ flows: [],
124
+ }),
125
+ ".gitignore": "build/\n.ingestron/\n.local/\n",
126
+ };
127
+ for (const name of environments)
128
+ files[`environments/${name}.yaml`] = stringify({
129
+ apiVersion: "ingestron.environment/v1",
130
+ environment: name,
131
+ values: {},
132
+ bindings: {},
133
+ });
134
+ for (const name of Object.keys(files))
135
+ check(!existsSync(resolve(root, name)), "OWNER", `Initialisation would replace existing ${name}`);
136
+ return preview(root, files);
137
+ }
138
+ let reference = options.provider;
139
+ if (!reference.includes("@")) {
140
+ const source = canonicalPackageReference(`${reference}@0.0.0`).split("@")[0];
141
+ const matches = installedProviders(root).filter((p) => p.reference.split("@")[0] === source);
142
+ check(matches.length > 0, "PACKAGE", `No installed provider ${reference}; use plugin install ${reference} first`);
143
+ check(matches.length === 1, "PACKAGE", `Multiple installed versions of ${reference}; use an exact reference: ${matches.map((p) => friendlyReference(p.reference)).join(", ")}`);
144
+ reference = matches[0].reference;
145
+ }
146
+ const files = providerAuthor(root, reference, { operation: "initialise", options: { ...options, environments } }, (name) => name === "project.yaml" ||
147
+ name === "README.md" ||
148
+ name === ".gitignore" ||
149
+ /^environments\/[\w-]+\.yaml$/.test(name));
150
+ check(files["project.yaml"], "PROVIDER", "Provider must author a project");
151
+ // Providers may return JSON (valid YAML); editable scaffolds use block YAML.
152
+ for (const [name, text] of Object.entries(files)) {
153
+ if (name.endsWith(".yaml"))
154
+ files[name] = stringify(yaml(text, name).toJS());
155
+ }
156
+ for (const name of Object.keys(files))
157
+ check(!existsSync(resolve(root, name)), "OWNER", `Initialisation would replace existing ${name}`);
158
+ return preview(root, files);
159
+ }
160
+ export function migrateProvider(root, options) {
161
+ const reader = new Configuration(root), doc = yaml(reader.text("project.yaml"), "project.yaml");
162
+ check(doc.hasIn(["providers", "packages", options.package]), "PACKAGE", "Unknown project package");
163
+ const selected = providerReference(root, options.to);
164
+ doc.setIn(["providers", "packages", options.package], {
165
+ source: selected.source,
166
+ version: selected.version,
167
+ });
168
+ return preview(root, { "project.yaml": doc.toString() });
169
+ }
170
+ function flowFile(root, id) {
171
+ const projectFile = resolve(root, "project.yaml"), project = yaml(readFileSync(projectFile, "utf8"), projectFile).toJS();
172
+ const reader = new Configuration(root);
173
+ for (const reference of project.flows ?? []) {
174
+ check(reference.$resolve, "AUTHORING", "Editable flows must be in their own referenced flow.yaml");
175
+ const file = reader.path(reference.$resolve);
176
+ const doc = yaml(reader.text(file), file);
177
+ if (doc.get("id") === id)
178
+ return { file, doc, projectFile, project };
179
+ }
180
+ throw new Problem("FLOW", `Unknown flow ${id}`);
181
+ }
182
+ export function addFlow(root, options) {
183
+ const reader = new Configuration(root), project = yaml(reader.text("project.yaml"), "project.yaml"), raw = project.toJS(), resolved = reader.load();
184
+ check(/^[A-Za-z_][\w-]*$/.test(options.id), "FLOW", "Invalid flow ID");
185
+ check(!resolved.flows.some((f) => f.id === options.id), "OWNER", `Flow ${options.id} already exists`);
186
+ const configuration = options.provider ?? resolved.defaults.provider;
187
+ const config = resolved.providers.configurations[configuration];
188
+ check(config, "PROVIDER", "Select a provider configuration for this flow");
189
+ const pkg = resolved.providers.packages[config.package];
190
+ const environments = Object.values(raw.environments).map((ref) => {
191
+ check(ref.$resolve, "AUTHORING", "Keep editable environments in referenced files");
192
+ const file = reader.path(ref.$resolve);
193
+ return {
194
+ path: relative(reader.root, file),
195
+ text: reader.text(file),
196
+ value: yaml(reader.text(file), file).toJS(),
197
+ };
198
+ });
199
+ const files = providerAuthor(root, `${pkg.source}@${pkg.version}`, {
200
+ operation: "flow",
201
+ options,
202
+ configuration,
203
+ project: resolved,
204
+ environments,
205
+ }, (name) => name.startsWith(`flows/${options.id}/`) ||
206
+ environments.some((e) => e.path === name));
207
+ const file = `flows/${options.id}/flow.yaml`;
208
+ check(files[file], "PROVIDER", "Provider must author the requested flow");
209
+ check(!existsSync(resolve(root, file)), "OWNER", `Existing flow file ${file}`);
210
+ project.set("flows", [...raw.flows, { $resolve: "./" + file }]);
211
+ files["project.yaml"] = project.toString();
212
+ return preview(root, files);
213
+ }
214
+ export function addTable(root, options) {
215
+ const { file, doc } = flowFile(root, options.flow);
216
+ check(doc.get("kind") === "ingestion", "FLOW", "table add requires an ingestion flow");
217
+ check(/^[A-Za-z_][\w-]*$/.test(options.id), "TABLE", "Invalid table ID");
218
+ check(!doc.hasIn(["tables", options.id]), "OWNER", `Table ${options.id} already exists`);
219
+ const reader = new Configuration(root), contract = reader.load(options.contract), target = `flows/${options.flow}/01-ingest/contracts/${options.id}.odcs.yaml`;
220
+ contractColumns(contract);
221
+ const table = {
222
+ contract: { $resolve: `./01-ingest/contracts/${options.id}.odcs.yaml` },
223
+ };
224
+ doc.setIn(["tables", options.id], table);
225
+ return preview(root, {
226
+ [relative(realpathSync(root), file)]: doc.toString(),
227
+ [target]: stringify(contract),
228
+ });
229
+ }
230
+ export function configureTable(root, flow, id, patch) {
231
+ const { file, doc } = flowFile(root, flow);
232
+ check(doc.hasIn(["tables", id]), "TABLE", `Unknown table ${id}`);
233
+ check(!("id" in patch) && !("contract" in patch), "AUTHORING", "Identity/contract changes require an explicit file edit");
234
+ doc.setIn(["tables", id], merge(doc.toJS().tables[id], patch));
235
+ return preview(root, {
236
+ [relative(realpathSync(root), file)]: doc.toString(),
237
+ });
238
+ }
239
+ export function addDataset(root, options) {
240
+ const { file, doc } = flowFile(root, options.flow);
241
+ check(doc.get("kind") === "transformation", "FLOW", "dataset add requires a transformation flow");
242
+ check(!doc.hasIn(["publishes", options.id]), "OWNER", "Dataset already exists");
243
+ const reader = new Configuration(root), contract = reader.load(options.contract);
244
+ contractColumns(contract);
245
+ const target = `flows/${options.flow}/contracts/${options.id}.odcs.yaml`;
246
+ doc.setIn(["publishes", options.id], {
247
+ from: options.from,
248
+ contract: { $resolve: `./contracts/${options.id}.odcs.yaml` },
249
+ });
250
+ return preview(root, {
251
+ [relative(realpathSync(root), file)]: doc.toString(),
252
+ [target]: stringify(contract),
253
+ });
254
+ }
255
+ export function addEnvironment(root, options) {
256
+ check(/^[A-Za-z_][\w-]*$/.test(options.name) &&
257
+ !["__proto__", "constructor", "prototype"].includes(options.name), "ENVIRONMENT", "Invalid environment identifier");
258
+ const reader = new Configuration(root);
259
+ const project = yaml(reader.text("project.yaml"), "project.yaml");
260
+ const raw = project.toJS();
261
+ check(raw.environments && !Object.hasOwn(raw.environments, options.name), "OWNER", `Environment ${options.name} already exists`);
262
+ check(Object.hasOwn(raw.environments, options.from), "ENVIRONMENT", `Unknown source environment ${options.from}`);
263
+ const source = raw.environments[options.from];
264
+ check(typeof source?.$resolve === "string", "ENVIRONMENT", "The source environment must be in a referenced file");
265
+ // Copy beside the source so every relative reference keeps its meaning.
266
+ const sourceFile = reader.path(source.$resolve);
267
+ const destination = resolve(dirname(sourceFile), `${options.name}.yaml`);
268
+ const target = relative(reader.root, destination);
269
+ fence(reader.root, target);
270
+ check(!existsSync(destination), "OWNER", `Existing environment file ${target}`);
271
+ const profile = yaml(reader.text(sourceFile), sourceFile);
272
+ check(profile.toJS()?.apiVersion === "ingestron.environment/v1", "ENVIRONMENT", "Expected an environment document");
273
+ profile.set("environment", options.name);
274
+ project.setIn(["environments", options.name], { $resolve: "./" + target });
275
+ return preview(root, {
276
+ [target]: profile.toString(),
277
+ "project.yaml": project.toString(),
278
+ });
279
+ }
280
+ export function editValue(root, environment, pointer, value) {
281
+ const reader = new Configuration(root), project = yaml(reader.text("project.yaml"), "project.yaml").toJS(), selected = project.environments[environment];
282
+ check(selected?.$resolve, "ENVIRONMENT", "Environment values must be in a referenced environment file for editing");
283
+ const file = reader.path(selected.$resolve), doc = yaml(reader.text(file), file);
284
+ const keys = pointer.split(".");
285
+ check(keys.length &&
286
+ ["values", "bindings"].includes(keys[0]) &&
287
+ keys.every((k) => /^[A-Za-z_][A-Za-z0-9_-]*$/.test(k)), "CONFIG", "Use a values.* or bindings.* field path");
288
+ doc.setIn(keys, value);
289
+ return preview(root, { [relative(reader.root, file)]: doc.toString() });
290
+ }
291
+ export function configureFlow(root, flow, patch) {
292
+ const { file, doc } = flowFile(root, flow);
293
+ check(!["apiVersion", "id", "kind", "tables", "steps"].some((k) => k in patch), "AUTHORING", "Use dedicated table/step commands for structural changes");
294
+ return preview(root, {
295
+ [relative(realpathSync(root), file)]: stringify(merge(doc.toJS(), patch)),
296
+ });
297
+ }
298
+ export function addStep(root, flow, step, after) {
299
+ const { file, doc } = flowFile(root, flow);
300
+ const value = step;
301
+ check(doc.toJS().kind === "transformation", "FLOW", "Ingestion steps are owned by the standard; use a transformation flow");
302
+ {
303
+ const steps = doc.toJS().steps ?? [];
304
+ check(!steps.some((s) => s.id === value.id), "OWNER", "Step already exists");
305
+ const at = after
306
+ ? steps.findIndex((s) => s.id === after)
307
+ : steps.length - 1;
308
+ check(!after || at >= 0, "STEP", `Unknown predecessor ${after}`);
309
+ steps.splice(at + 1, 0, step);
310
+ doc.set("steps", steps);
311
+ }
312
+ return preview(root, {
313
+ [relative(realpathSync(root), file)]: doc.toString(),
314
+ });
315
+ }
@@ -0,0 +1,34 @@
1
+ import { z } from "zod";
2
+ export type Location = {
3
+ file: string;
4
+ pointer: string;
5
+ line: number;
6
+ column: number;
7
+ };
8
+ export declare class Configuration {
9
+ readonly environmentVariables: Record<string, string | undefined>;
10
+ readonly root: string;
11
+ readonly files: Record<string, string>;
12
+ readonly locations: WeakMap<object, Location>;
13
+ readonly inputs: Record<string, string>;
14
+ readonly pending: {
15
+ name: string;
16
+ file?: string;
17
+ pointer?: string;
18
+ }[];
19
+ private bytes;
20
+ private nodes;
21
+ private documents;
22
+ constructor(root: string, environmentVariables?: Record<string, string | undefined>);
23
+ path(name: string, from?: string): string;
24
+ text(name: string, from?: string): string;
25
+ private document;
26
+ load(name?: string, from?: string, stack?: string[], depth?: number): any;
27
+ private walk;
28
+ parse<T>(schema: z.ZodType<T>, value: unknown): T;
29
+ values(value: any, context: Record<string, any>, options?: {
30
+ draft?: boolean;
31
+ table?: boolean;
32
+ runtime?: boolean;
33
+ }): any;
34
+ }