@ingestron/cli 0.0.0-stage → 1.1.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 (175) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +4 -0
  3. package/README.md +43 -2
  4. package/dist/args.d.ts +105 -0
  5. package/dist/args.d.ts.map +1 -0
  6. package/dist/args.js +161 -0
  7. package/dist/args.js.map +1 -0
  8. package/dist/ask.d.ts +5 -0
  9. package/dist/ask.d.ts.map +1 -0
  10. package/dist/ask.js +17 -0
  11. package/dist/ask.js.map +1 -0
  12. package/dist/compare.d.ts +13 -0
  13. package/dist/compare.d.ts.map +1 -0
  14. package/dist/compare.js +92 -0
  15. package/dist/compare.js.map +1 -0
  16. package/dist/environments.d.ts +6 -0
  17. package/dist/environments.d.ts.map +1 -0
  18. package/dist/environments.js +25 -0
  19. package/dist/environments.js.map +1 -0
  20. package/dist/generate.d.ts +16 -0
  21. package/dist/generate.d.ts.map +1 -0
  22. package/dist/generate.js +118 -0
  23. package/dist/generate.js.map +1 -0
  24. package/dist/index.d.ts +21 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +46 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/init-choice.d.ts +10 -0
  29. package/dist/init-choice.d.ts.map +1 -0
  30. package/dist/init-choice.js +36 -0
  31. package/dist/init-choice.js.map +1 -0
  32. package/dist/init-preflight.d.ts +4 -0
  33. package/dist/init-preflight.d.ts.map +1 -0
  34. package/dist/init-preflight.js +18 -0
  35. package/dist/init-preflight.js.map +1 -0
  36. package/dist/init.d.ts +5 -0
  37. package/dist/init.d.ts.map +1 -0
  38. package/dist/init.js +136 -0
  39. package/dist/init.js.map +1 -0
  40. package/dist/io.d.ts +8 -0
  41. package/dist/io.d.ts.map +1 -0
  42. package/dist/io.js +3 -0
  43. package/dist/io.js.map +1 -0
  44. package/dist/main.d.ts +3 -0
  45. package/dist/main.d.ts.map +1 -0
  46. package/dist/main.js +11 -0
  47. package/dist/main.js.map +1 -0
  48. package/dist/outputs.d.ts +12 -0
  49. package/dist/outputs.d.ts.map +1 -0
  50. package/dist/outputs.js +53 -0
  51. package/dist/outputs.js.map +1 -0
  52. package/dist/packs.d.ts +4 -0
  53. package/dist/packs.d.ts.map +1 -0
  54. package/dist/packs.js +5 -0
  55. package/dist/packs.js.map +1 -0
  56. package/dist/project-choices.d.ts +27 -0
  57. package/dist/project-choices.d.ts.map +1 -0
  58. package/dist/project-choices.js +44 -0
  59. package/dist/project-choices.js.map +1 -0
  60. package/dist/project-questions.d.ts +5 -0
  61. package/dist/project-questions.d.ts.map +1 -0
  62. package/dist/project-questions.js +43 -0
  63. package/dist/project-questions.js.map +1 -0
  64. package/dist/project-recipes.d.ts +14 -0
  65. package/dist/project-recipes.d.ts.map +1 -0
  66. package/dist/project-recipes.js +115 -0
  67. package/dist/project-recipes.js.map +1 -0
  68. package/dist/request.d.ts +13 -0
  69. package/dist/request.d.ts.map +1 -0
  70. package/dist/request.js +114 -0
  71. package/dist/request.js.map +1 -0
  72. package/dist/sample-contract.d.ts +11 -0
  73. package/dist/sample-contract.d.ts.map +1 -0
  74. package/dist/sample-contract.js +91 -0
  75. package/dist/sample-contract.js.map +1 -0
  76. package/dist/sample-read.d.ts +8 -0
  77. package/dist/sample-read.d.ts.map +1 -0
  78. package/dist/sample-read.js +89 -0
  79. package/dist/sample-read.js.map +1 -0
  80. package/dist/samples.d.ts +33 -0
  81. package/dist/samples.d.ts.map +1 -0
  82. package/dist/samples.js +108 -0
  83. package/dist/samples.js.map +1 -0
  84. package/dist/scaffold.d.ts +34 -0
  85. package/dist/scaffold.d.ts.map +1 -0
  86. package/dist/scaffold.js +106 -0
  87. package/dist/scaffold.js.map +1 -0
  88. package/dist/schemas.d.ts +5 -0
  89. package/dist/schemas.d.ts.map +1 -0
  90. package/dist/schemas.js +39 -0
  91. package/dist/schemas.js.map +1 -0
  92. package/dist/sources.d.ts +17 -0
  93. package/dist/sources.d.ts.map +1 -0
  94. package/dist/sources.js +103 -0
  95. package/dist/sources.js.map +1 -0
  96. package/dist/templates.d.ts +64 -0
  97. package/dist/templates.d.ts.map +1 -0
  98. package/dist/templates.js +88 -0
  99. package/dist/templates.js.map +1 -0
  100. package/package.json +68 -3
  101. package/project-templates/kafka/README.md +12 -0
  102. package/project-templates/kafka/connections/events.yaml +8 -0
  103. package/project-templates/kafka/contracts/page_views.yaml +13 -0
  104. package/project-templates/kafka/contracts/purchases.yaml +13 -0
  105. package/project-templates/kafka/flows/events.yaml +10 -0
  106. package/project-templates/kafka/project.yaml +10 -0
  107. package/project-templates/kafka/standard.yaml +4 -0
  108. package/project-templates/kafka/template.yaml +3 -0
  109. package/project-templates/minimal/README.md +10 -0
  110. package/project-templates/minimal/connections/shop.yaml +7 -0
  111. package/project-templates/minimal/contracts/orders.yaml +14 -0
  112. package/project-templates/minimal/flows/orders.yaml +6 -0
  113. package/project-templates/minimal/project.yaml +11 -0
  114. package/project-templates/minimal/standard.yaml +4 -0
  115. package/project-templates/minimal/template.yaml +7 -0
  116. package/project-templates/private-files/README.md +29 -0
  117. package/project-templates/private-files/connections/share.yaml +10 -0
  118. package/project-templates/private-files/contracts/sales.yaml +14 -0
  119. package/project-templates/private-files/contracts/stock.yaml +14 -0
  120. package/project-templates/private-files/flows/stores.yaml +9 -0
  121. package/project-templates/private-files/platform.yaml +11 -0
  122. package/project-templates/private-files/project.yaml +19 -0
  123. package/project-templates/private-files/standard.yaml +11 -0
  124. package/project-templates/private-files/template.yaml +10 -0
  125. package/project-templates/private-sql-server/README.md +25 -0
  126. package/project-templates/private-sql-server/connections/erp.yaml +10 -0
  127. package/project-templates/private-sql-server/contracts/customers.yaml +14 -0
  128. package/project-templates/private-sql-server/contracts/invoices.yaml +15 -0
  129. package/project-templates/private-sql-server/flows/erp.yaml +13 -0
  130. package/project-templates/private-sql-server/platform.yaml +11 -0
  131. package/project-templates/private-sql-server/project.yaml +19 -0
  132. package/project-templates/private-sql-server/standard.yaml +17 -0
  133. package/project-templates/private-sql-server/template.yaml +10 -0
  134. package/project-templates/pyspark/README.md +12 -0
  135. package/project-templates/pyspark/connections/billing.yaml +7 -0
  136. package/project-templates/pyspark/contracts/invoices.yaml +16 -0
  137. package/project-templates/pyspark/contracts/monthly_revenue.yaml +14 -0
  138. package/project-templates/pyspark/flows/billing.yaml +6 -0
  139. package/project-templates/pyspark/project.yaml +11 -0
  140. package/project-templates/pyspark/standard.yaml +4 -0
  141. package/project-templates/pyspark/template.yaml +3 -0
  142. package/project-templates/pyspark/transforms/monthly_revenue.py +10 -0
  143. package/project-templates/pyspark/transforms/monthly_revenue.yaml +10 -0
  144. package/project-templates/retail/README.md +22 -0
  145. package/project-templates/retail/connections/shop.yaml +8 -0
  146. package/project-templates/retail/contracts/customers.yaml +12 -0
  147. package/project-templates/retail/contracts/daily_sales.yaml +19 -0
  148. package/project-templates/retail/contracts/orders.yaml +16 -0
  149. package/project-templates/retail/flows/shop.yaml +18 -0
  150. package/project-templates/retail/platform.yaml +21 -0
  151. package/project-templates/retail/project.yaml +18 -0
  152. package/project-templates/retail/standard.yaml +15 -0
  153. package/project-templates/retail/template.yaml +10 -0
  154. package/project-templates/retail/transforms/daily_sales.sql +7 -0
  155. package/project-templates/retail/transforms/daily_sales.yaml +7 -0
  156. package/project-templates/salesforce/README.md +6 -0
  157. package/project-templates/salesforce/connections/salesforce.yaml +7 -0
  158. package/project-templates/salesforce/contracts/accounts.yaml +13 -0
  159. package/project-templates/salesforce/contracts/opportunities.yaml +14 -0
  160. package/project-templates/salesforce/flows/salesforce.yaml +7 -0
  161. package/project-templates/salesforce/project.yaml +9 -0
  162. package/project-templates/salesforce/standard.yaml +4 -0
  163. package/project-templates/salesforce/template.yaml +3 -0
  164. package/project-templates/sql-server/README.md +17 -0
  165. package/project-templates/sql-server/connections/erp.yaml +8 -0
  166. package/project-templates/sql-server/contracts/customer_balances.yaml +13 -0
  167. package/project-templates/sql-server/contracts/customers.yaml +13 -0
  168. package/project-templates/sql-server/contracts/invoices.yaml +14 -0
  169. package/project-templates/sql-server/flows/erp.yaml +13 -0
  170. package/project-templates/sql-server/platform.yaml +6 -0
  171. package/project-templates/sql-server/project.yaml +13 -0
  172. package/project-templates/sql-server/standard.yaml +9 -0
  173. package/project-templates/sql-server/template.yaml +3 -0
  174. package/project-templates/sql-server/transforms/customer_balances.sql +5 -0
  175. package/project-templates/sql-server/transforms/customer_balances.yaml +6 -0
@@ -0,0 +1,103 @@
1
+ // Where templates and standards come from: a built-in name, a folder, or a
2
+ // Git URL cloned with the user's own git. Nothing from a source is run.
3
+ import { execFileSync } from "node:child_process";
4
+ import { existsSync, lstatSync, mkdtempSync, realpathSync, rmSync } from "node:fs";
5
+ import { tmpdir } from "node:os";
6
+ import { basename, join, relative, resolve } from "node:path";
7
+ import { builtInTemplates, readTemplate } from "./templates.js";
8
+ /** The URL without `user:password@`, for messages and the standard's "From" line. */
9
+ export function withoutCredentials(text) {
10
+ return text.replace(/(\/\/)[^/@\s]+@/g, "$1");
11
+ }
12
+ /** Sources come from the user's command line; one starting with "-" could pass for a git option. */
13
+ function refuseOption(spec) {
14
+ if (spec.startsWith("-"))
15
+ throw new Error(`A template or standard cannot start with "-": ${spec}.`);
16
+ }
17
+ /** A Git URL rather than a path or a name. */
18
+ export function isGitUrl(spec) {
19
+ return (/^(https?|ssh|file|git):\/\//.test(spec) ||
20
+ /^[\w.-]+@[\w.-]+:/.test(spec) ||
21
+ /\.git(#|$)/.test(spec));
22
+ }
23
+ /** A template from a built-in name, a folder or a Git URL (`#` names a sub-folder). Call `done()` when finished. */
24
+ export function templateSource(spec, cwd) {
25
+ refuseOption(spec);
26
+ if (isGitUrl(spec)) {
27
+ const { path, done } = cloned(spec, "folder");
28
+ try {
29
+ return { template: readTemplate(path, basename(path)), done };
30
+ }
31
+ catch (error) {
32
+ done();
33
+ throw error;
34
+ }
35
+ }
36
+ const folder = resolve(cwd, spec);
37
+ if (spec.includes("/") || spec.startsWith(".")) {
38
+ if (!existsSync(folder))
39
+ throw new Error(`No template folder ${spec}.`);
40
+ return { template: readTemplate(folder, basename(folder)), done: () => undefined };
41
+ }
42
+ const builtIns = builtInTemplates();
43
+ const template = builtIns.find((item) => item.name === spec);
44
+ if (!template) {
45
+ const names = builtIns.map((item) => item.name).join(", ");
46
+ throw new Error(`No template "${spec}". Built-in templates: ${names}.`);
47
+ }
48
+ return { template, done: () => undefined };
49
+ }
50
+ /** A standard file from a path or a Git URL with `#path/to/standard.yaml`. */
51
+ export function standardSource(spec, cwd) {
52
+ if (!isGitUrl(spec)) {
53
+ const file = resolve(cwd, spec);
54
+ if (!existsSync(file))
55
+ throw new Error(`No standard file ${spec}.`);
56
+ return { file, source: spec, done: () => undefined };
57
+ }
58
+ const { path, done } = cloned(spec, "file");
59
+ return { file: path, source: withoutCredentials(spec), done };
60
+ }
61
+ /**
62
+ * Clones the URL before `#` into a temporary folder and returns the entry
63
+ * after `#`, which must really be inside the clone (no links) and be a
64
+ * regular file or folder as asked.
65
+ */
66
+ function cloned(spec, kind) {
67
+ const [url = "", inside = ""] = spec.split("#");
68
+ if (inside.split("/").includes("..") || inside.startsWith("/"))
69
+ throw new Error(`The part after "#" must stay inside the repository: ${inside}.`);
70
+ const folder = mkdtempSync(join(tmpdir(), "ingestron-template-"));
71
+ const done = () => rmSync(folder, { recursive: true, force: true });
72
+ try {
73
+ clone(url, folder);
74
+ return { path: checkedEntry(folder, inside, kind), done };
75
+ }
76
+ catch (error) {
77
+ done();
78
+ throw error;
79
+ }
80
+ }
81
+ function clone(url, folder) {
82
+ // No hooks, no submodules; "--" so the URL can never be read as an option.
83
+ const args = ["-c", "core.hooksPath=/dev/null", "clone", "--depth", "1", "--quiet"];
84
+ try {
85
+ execFileSync("git", [...args, "--no-recurse-submodules", "--", url, folder], { stdio: "pipe" });
86
+ }
87
+ catch (error) {
88
+ const reason = error.stderr?.toString().trim() ?? String(error);
89
+ throw new Error(withoutCredentials(`Could not clone ${url}: ${reason}`));
90
+ }
91
+ }
92
+ function checkedEntry(folder, inside, kind) {
93
+ const path = join(folder, inside);
94
+ const stat = lstatSync(path, { throwIfNoEntry: false });
95
+ if (!stat)
96
+ throw new Error(`The repository has no "${inside}".`);
97
+ const real = relative(realpathSync(folder), realpathSync(path));
98
+ const isKind = kind === "file" ? stat.isFile() : stat.isDirectory();
99
+ if (!isKind || real.startsWith(".."))
100
+ throw new Error(`"${inside || "."}" in the repository is not a ${kind === "file" ? "regular file" : "folder"}.`);
101
+ return path;
102
+ }
103
+ //# sourceMappingURL=sources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sources.js","sourceRoot":"","sources":["../src/sources.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /** One question; the answer becomes `values.<value>` in project.yaml. */
2
+ export interface Question {
3
+ value: string;
4
+ ask: string;
5
+ default: string;
6
+ }
7
+ /** A template folder and what it asks. */
8
+ export interface Template {
9
+ name: string;
10
+ folder: string;
11
+ title: string;
12
+ summary: string;
13
+ questions: Question[];
14
+ }
15
+ /** template.yaml's schema, for editors; `readTemplate` checks the same rules. */
16
+ export declare const templateSchema: {
17
+ title: string;
18
+ type: string;
19
+ additionalProperties: boolean;
20
+ required: string[];
21
+ properties: {
22
+ title: {
23
+ type: string;
24
+ description: string;
25
+ };
26
+ summary: {
27
+ type: string;
28
+ description: string;
29
+ };
30
+ questions: {
31
+ type: string;
32
+ description: string;
33
+ items: {
34
+ type: string;
35
+ additionalProperties: boolean;
36
+ required: string[];
37
+ properties: {
38
+ value: {
39
+ type: string;
40
+ pattern: string;
41
+ description: string;
42
+ };
43
+ ask: {
44
+ type: string;
45
+ description: string;
46
+ };
47
+ default: {
48
+ type: string;
49
+ description: string;
50
+ };
51
+ };
52
+ };
53
+ };
54
+ };
55
+ };
56
+ /** Where this package's built-ins are. */
57
+ export declare const builtInFolder: string;
58
+ /** Built-in templates: the starters, each named by its folder. */
59
+ export declare function builtInTemplates(): Template[];
60
+ /** Reads and checks `<folder>/template.yaml`; throws an Error with the reason. */
61
+ export declare function readTemplate(folder: string, name: string): Template;
62
+ /** Checks template.yaml's text; throws an Error with the reason. */
63
+ export declare function parseTemplate(text: string, name: string, folder: string): Template;
64
+ //# sourceMappingURL=templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAOA,yEAAyE;AACzE,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,0CAA0C;AAC1C,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED,iFAAiF;AACjF,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+B1B,CAAC;AAEF,0CAA0C;AAC1C,eAAO,MAAM,aAAa,QAAkB,CAAC;AAE7C,kEAAkE;AAClE,wBAAgB,gBAAgB,IAAI,QAAQ,EAAE,CAM7C;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,CAOnE;AAED,oEAAoE;AACpE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ,CAalF"}
@@ -0,0 +1,88 @@
1
+ // Project templates: an ordinary project folder plus template.yaml, which
2
+ // says what to ask. Built-in templates are the samples' starters.
3
+ import { existsSync, lstatSync, readdirSync, readFileSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { parse } from "yaml";
6
+ import { templatesFolder } from "./samples.js";
7
+ /** template.yaml's schema, for editors; `readTemplate` checks the same rules. */
8
+ export const templateSchema = {
9
+ title: "Ingestron template",
10
+ type: "object",
11
+ additionalProperties: false,
12
+ required: ["title", "summary"],
13
+ properties: {
14
+ title: { type: "string", description: "The template's name, as `ingestron init` lists it." },
15
+ summary: { type: "string", description: "One sentence on what the template sets up." },
16
+ questions: {
17
+ type: "array",
18
+ description: "What `ingestron init` asks; each answer becomes values.<value> in project.yaml.",
19
+ items: {
20
+ type: "object",
21
+ additionalProperties: false,
22
+ required: ["value", "ask", "default"],
23
+ properties: {
24
+ value: {
25
+ type: "string",
26
+ pattern: "^[a-z_][a-z0-9_]*$",
27
+ description: "The key in project.yaml values.",
28
+ },
29
+ ask: { type: "string", description: "The question, as the user sees it." },
30
+ default: {
31
+ type: "string",
32
+ description: "The answer used with --yes or an empty answer.",
33
+ },
34
+ },
35
+ },
36
+ },
37
+ },
38
+ };
39
+ /** Where this package's built-ins are. */
40
+ export const builtInFolder = templatesFolder;
41
+ /** Built-in templates: the starters, each named by its folder. */
42
+ export function builtInTemplates() {
43
+ return readdirSync(builtInFolder, { withFileTypes: true })
44
+ .filter((entry) => entry.isDirectory())
45
+ .filter((entry) => existsSync(join(builtInFolder, entry.name, "template.yaml")))
46
+ .map((entry) => readTemplate(join(builtInFolder, entry.name), entry.name))
47
+ .sort((a, b) => a.name.localeCompare(b.name));
48
+ }
49
+ /** Reads and checks `<folder>/template.yaml`; throws an Error with the reason. */
50
+ export function readTemplate(folder, name) {
51
+ for (const required of ["template.yaml", "project.yaml"]) {
52
+ const stat = lstatSync(join(folder, required), { throwIfNoEntry: false });
53
+ if (!stat)
54
+ throw new Error(`Template "${name}" has no ${required}.`);
55
+ if (!stat.isFile())
56
+ throw new Error(`Template "${name}": ${required} is not a regular file.`);
57
+ }
58
+ return parseTemplate(readFileSync(join(folder, "template.yaml"), "utf8"), name, folder);
59
+ }
60
+ /** Checks template.yaml's text; throws an Error with the reason. */
61
+ export function parseTemplate(text, name, folder) {
62
+ const data = parse(text);
63
+ const unknown = Object.keys(data ?? {}).find((key) => !["title", "summary", "questions"].includes(key));
64
+ if (unknown)
65
+ throw new Error(`template.yaml: unknown key "${unknown}".`);
66
+ return {
67
+ name,
68
+ folder,
69
+ title: String(data.title ?? name),
70
+ summary: String(data.summary ?? ""),
71
+ questions: questionsOf(data.questions),
72
+ };
73
+ }
74
+ function questionsOf(value) {
75
+ if (value === undefined)
76
+ return [];
77
+ if (!Array.isArray(value))
78
+ throw new Error("template.yaml: questions must be a list.");
79
+ return value.map((item, index) => {
80
+ const keys = ["value", "ask", "default"];
81
+ if (!keys.every((key) => typeof item?.[key] === "string"))
82
+ throw new Error(`template.yaml: question ${index + 1} needs "value", "ask" and "default".`);
83
+ if (!/^[a-z_][a-z0-9_]*$/.test(String(item.value)))
84
+ throw new Error(`template.yaml: question ${index + 1}'s value "${String(item.value)}" is not a valid name.`);
85
+ return { value: String(item.value), ask: String(item.ask), default: String(item.default) };
86
+ });
87
+ }
88
+ //# sourceMappingURL=templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,71 @@
1
1
  {
2
2
  "name": "@ingestron/cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.1.0",
4
+ "description": "ingestron plan | apply | check | schemas: generate platform assets from definitions.",
5
+ "license": "Apache-2.0",
6
+ "author": "IntentLabs <hello@ingestron.io>",
7
+ "homepage": "https://docs.ingestron.io",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/ingestron/ingestron.git",
11
+ "directory": "packages/cli"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/ingestron/ingestron/issues",
15
+ "email": "hello@ingestron.io"
16
+ },
17
+ "type": "module",
18
+ "engines": {
19
+ "node": ">=22.13.0"
20
+ },
21
+ "bin": {
22
+ "ingestron": "./dist/main.js"
23
+ },
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "import": "./dist/index.js"
28
+ }
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "README.md",
33
+ "project-templates",
34
+ "NOTICE"
35
+ ],
36
+ "dependencies": {
37
+ "@ingestron/pack-adf": "^1.1.0",
38
+ "@ingestron/pack-databricks": "^1.0.0",
39
+ "yaml": "2.9.1",
40
+ "@ingestron/config": "^1.0.0",
41
+ "@ingestron/definitions": "^1.1.0",
42
+ "@ingestron/generate": "^1.0.0"
43
+ },
44
+ "devDependencies": {
45
+ "@microsoft/api-extractor": "7.59.4",
46
+ "@types/node": "22.20.5",
47
+ "ajv": "8.20.0",
48
+ "eslint": "10.13.0",
49
+ "eslint-plugin-jsdoc": "65.4.2",
50
+ "tsx": "4.23.15",
51
+ "typescript": "6.0.3",
52
+ "typescript-eslint": "8.72.0",
53
+ "vitest": "5.0.5",
54
+ "vscode-languageserver-textdocument": "1.0.12",
55
+ "yaml-language-server": "1.25.0"
56
+ },
57
+ "publishConfig": {
58
+ "access": "public"
59
+ },
60
+ "scripts": {
61
+ "build": "tsc -p tsconfig.build.json",
62
+ "typecheck": "pnpm build && tsc -p tsconfig.json --noEmit",
63
+ "lint": "eslint .",
64
+ "test": "vitest run",
65
+ "check:docs": "tsx scripts/check-docs.ts",
66
+ "validate": "pnpm lint && pnpm typecheck && pnpm test && pnpm check:docs && pnpm check:api",
67
+ "check:bundle": "tsx scripts/check-bundle-schema.ts",
68
+ "api": "api-extractor run --local",
69
+ "check:api": "api-extractor run"
70
+ }
6
71
  }
@@ -0,0 +1,12 @@
1
+ # Kafka
2
+
3
+ Two topics of an Event Hubs namespace, read through its Kafka endpoint.
4
+ [The ingestion pipeline](build/dev/resources/clickstream_events_ingestion.pipeline.yml)
5
+ runs on the `PREVIEW` channel, appends every message, and parses JSON values
6
+ with the contract's column types. In production it runs continuously; in
7
+ `dev` it runs when started (`databricks bundle run`), which costs less. Set
8
+ `continuous: true` to change that. The pack chose `mode: append`
9
+ for Kafka by its own rule.
10
+
11
+ Lakeflow Connect for Kafka is in Beta on Databricks (checked 2026-10-25); the
12
+ generated file says so.
@@ -0,0 +1,8 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Connection
3
+ id: events
4
+ description: Event Hubs namespace, used through its Kafka endpoint.
5
+ connector: kafka
6
+ connectivity: public
7
+ settings:
8
+ uc_connection: web_events_kafka
@@ -0,0 +1,13 @@
1
+ apiVersion: v3.1.0
2
+ kind: DataContract
3
+ id: web.page_views
4
+ version: 1.0.0
5
+ status: active
6
+ schema:
7
+ - name: page_views
8
+ logicalType: object
9
+ description: One event per page view.
10
+ properties:
11
+ - { name: session_id, logicalType: string, required: true }
12
+ - { name: url, logicalType: string }
13
+ - { name: viewed_at, logicalType: timestamp }
@@ -0,0 +1,13 @@
1
+ apiVersion: v3.1.0
2
+ kind: DataContract
3
+ id: web.purchases
4
+ version: 1.0.0
5
+ status: active
6
+ schema:
7
+ - name: purchases
8
+ logicalType: object
9
+ description: One event per purchase.
10
+ properties:
11
+ - { name: order_id, logicalType: string, required: true }
12
+ - { name: amount, logicalType: number, physicalType: "DECIMAL(18,2)" }
13
+ - { name: purchased_at, logicalType: timestamp }
@@ -0,0 +1,10 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Flow
3
+ id: events
4
+ source: events
5
+ ingest:
6
+ # Keep append explicit when this flow is composed with a chosen ingestion method.
7
+ with: { starting_offset: earliest, mode: append }
8
+ tables:
9
+ page_views: { object: web.page-views, contract: web.page_views }
10
+ purchases: { object: web.purchases, contract: web.purchases }
@@ -0,0 +1,10 @@
1
+ # Kafka topics streamed continuously with Lakeflow Connect (Beta), JSON
2
+ # values parsed with the contract's types.
3
+ id: clickstream
4
+ environments:
5
+ dev: {}
6
+ imports:
7
+ - standard.yaml
8
+ - connections/*.yaml
9
+ - flows/*.yaml
10
+ - contracts/*.yaml
@@ -0,0 +1,4 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Standard
3
+ id: web
4
+ packs: [databricks@1]
@@ -0,0 +1,3 @@
1
+ # Used by `ingestron init --template kafka`.
2
+ title: Kafka through Lakeflow Connect
3
+ summary: Two Kafka topics appended and parsed with the contract's column types.
@@ -0,0 +1,10 @@
1
+ # Minimal
2
+
3
+ The standard only names the pack, so every choice is a pack default.
4
+
5
+ | Definition | Becomes |
6
+ | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
7
+ | `flows/orders.yaml` | [a streaming table](build/dev/src/shop/bronze/orders.sql) reading new files once each |
8
+ | `contracts/orders.yaml` | `schemaHints` from the column types, `COMMENT`, and a not-null check on the required `id` |
9
+ | pack defaults | catalog `dev` (the environment), schema `bronze` (the first layer), `CLUSTER BY AUTO`, parquet |
10
+ | `project.yaml` | [a bundle](build/dev/databricks.yml) with one `dev` target in development mode, and [a serverless pipeline](build/dev/resources/shop.pipeline.yml) |
@@ -0,0 +1,7 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Connection
3
+ id: shop
4
+ connector: files
5
+ connectivity: public
6
+ settings:
7
+ path: ${values.landing}
@@ -0,0 +1,14 @@
1
+ apiVersion: v3.1.0
2
+ kind: DataContract
3
+ id: shop.orders
4
+ version: 1.0.0
5
+ status: active
6
+ schema:
7
+ - name: orders
8
+ logicalType: object
9
+ description: One row per order.
10
+ properties:
11
+ - { name: id, logicalType: integer, required: true, primaryKey: true }
12
+ - { name: customer_id, logicalType: integer }
13
+ - { name: total, logicalType: number, physicalType: "DECIMAL(18,2)" }
14
+ - { name: ordered_at, logicalType: timestamp }
@@ -0,0 +1,6 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Flow
3
+ id: orders
4
+ source: shop
5
+ tables:
6
+ orders: { object: orders, contract: shop.orders }
@@ -0,0 +1,11 @@
1
+ # The smallest project: everything not written here comes from the pack's defaults.
2
+ id: shop
3
+ values:
4
+ landing: /Volumes/dev/landing/shop
5
+ environments:
6
+ dev: {}
7
+ imports:
8
+ - standard.yaml
9
+ - connections/*.yaml
10
+ - flows/*.yaml
11
+ - contracts/*.yaml
@@ -0,0 +1,4 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Standard
3
+ id: defaults
4
+ packs: [databricks@1]
@@ -0,0 +1,7 @@
1
+ # Used by `ingestron init --template minimal`.
2
+ title: Minimal project
3
+ summary: "Pack defaults only: an Auto Loader streaming table with schema hints and clustering."
4
+ questions:
5
+ - value: landing
6
+ ask: "Where do the source files land?"
7
+ default: /Volumes/dev/landing/shop
@@ -0,0 +1,29 @@
1
+ # Private file share
2
+
3
+ Each store exports its sales and stock as CSV files to a share inside the
4
+ corporate network, `\\fs01\exports`, which the workspace cannot reach
5
+ (`connectivity: private`). The standard uses two packs,
6
+ `[databricks@1, adf@1]`. Databricks' `files` method cannot reach a private
7
+ source, so the ADF pack's rule chooses `adf-files`:
8
+
9
+ - **ADF** ([build/prod/adf](build/prod/adf/)), in the folder layout ADF
10
+ keeps in Git:
11
+ - a [file server linked service](build/prod/adf/linkedService/ls_stores_share.json)
12
+ through the self-hosted integration runtime `shir-stores`, with the
13
+ password from Key Vault;
14
+ - a [pipeline](build/prod/adf/pipeline/pl_stores_share.json) with one
15
+ binary copy per table: the files changed in the window, from
16
+ `\\fs01\exports\sales` to `<landing>/sales/`, keeping their names and
17
+ folders;
18
+ - an hourly [tumbling window trigger](build/prod/adf/trigger/tr_stores_share.json),
19
+ started in production and stopped elsewhere.
20
+ - **Databricks** ([build/prod/databricks](build/prod/databricks/)): streaming
21
+ tables that read the landing folders with Auto Loader, as CSV with a
22
+ header, typed by the contracts' columns.
23
+
24
+ The files are copied as they are, so the flow says their format
25
+ (`format: csv`). To copy every file each window instead of the changed ones,
26
+ set `mode: all`; to copy only some, set `files`, such as `"*.csv"`.
27
+
28
+ The integration runtime and Key Vault are shared infrastructure: they must
29
+ exist and are only referenced.
@@ -0,0 +1,10 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Connection
3
+ id: share
4
+ description: The stores' export share, inside the corporate network only.
5
+ connector: files
6
+ connectivity: private
7
+ settings:
8
+ path: '\\fs01\exports'
9
+ user: corp\ingest
10
+ password: ${secret:fs01-password} # a Key Vault secret name
@@ -0,0 +1,14 @@
1
+ apiVersion: v3.1.0
2
+ kind: DataContract
3
+ id: stores.sales
4
+ version: 1.0.0
5
+ status: active
6
+ schema:
7
+ - name: sales
8
+ logicalType: object
9
+ description: One row per sale, exported by each store every hour.
10
+ properties:
11
+ - { name: sale_id, logicalType: string, required: true, primaryKey: true }
12
+ - { name: store_id, logicalType: integer, required: true }
13
+ - { name: amount, logicalType: number, physicalType: "DECIMAL(18,2)" }
14
+ - { name: sold_at, logicalType: timestamp, required: true }
@@ -0,0 +1,14 @@
1
+ apiVersion: v3.1.0
2
+ kind: DataContract
3
+ id: stores.stock
4
+ version: 1.0.0
5
+ status: active
6
+ schema:
7
+ - name: stock
8
+ logicalType: object
9
+ description: Stock on hand per store and product, exported every hour.
10
+ properties:
11
+ - { name: store_id, logicalType: integer, required: true }
12
+ - { name: product_id, logicalType: integer, required: true }
13
+ - { name: on_hand, logicalType: integer }
14
+ - { name: counted_at, logicalType: timestamp, required: true }
@@ -0,0 +1,9 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Flow
3
+ id: stores
4
+ source: share
5
+ ingest:
6
+ with: { format: csv, options: { header: true } } # the files' own format
7
+ tables:
8
+ sales: { object: sales, contract: stores.sales }
9
+ stock: { object: stock, contract: stores.stock }
@@ -0,0 +1,11 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Platform
3
+ id: lakehouse
4
+ schedule:
5
+ cron: 0 15 * * * ?
6
+ timezone: Pacific/Auckland
7
+ settings:
8
+ adf-files: # shared infrastructure, referenced by name
9
+ landing: ${values.landing}
10
+ key_vault: ${values.key_vault}
11
+ integration_runtime: shir-stores
@@ -0,0 +1,19 @@
1
+ # Store exports on a file share inside the corporate network. ADF copies new
2
+ # files through a self-hosted integration runtime into a landing folder as
3
+ # they are; Databricks reads them there with Auto Loader.
4
+ id: stores
5
+ environments:
6
+ dev: {}
7
+ prod:
8
+ values:
9
+ landing: abfss://landing@storeslakeprod.dfs.core.windows.net/stores
10
+ key_vault: https://kv-stores-prod.vault.azure.net
11
+ values:
12
+ landing: abfss://landing@storeslakedev.dfs.core.windows.net/stores
13
+ key_vault: https://kv-stores-dev.vault.azure.net
14
+ imports:
15
+ - standard.yaml
16
+ - platform.yaml
17
+ - connections/*.yaml
18
+ - flows/*.yaml
19
+ - contracts/*.yaml
@@ -0,0 +1,11 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Standard
3
+ id: stores
4
+ packs: [databricks@1, adf@1] # Databricks first: it provides the defaults
5
+ naming:
6
+ catalog: stores_{{ environment }}
7
+ ingest:
8
+ settings:
9
+ adf-files: # the schedule; where it lands is the platform's
10
+ every: { frequency: Hour, interval: 1 }
11
+ start: 2026-10-01T00:00:00Z