@multiplatform.one/cli 7.11.0 → 7.15.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 (163) hide show
  1. package/README.md +121 -111
  2. package/bin/createMultiplatformApp.mjs +39 -37
  3. package/lib/bin/multiplatformOne.mjs +65 -75
  4. package/lib/commands/addApp.mjs +78 -0
  5. package/lib/commands/adoptApp.mjs +11 -7
  6. package/lib/commands/doctypeTypes.mjs +148 -0
  7. package/lib/commands/e2e.mjs +2 -2
  8. package/lib/commands/init.mjs +142 -459
  9. package/lib/commands/merge.mjs +359 -0
  10. package/lib/commands/pieces.mjs +236 -0
  11. package/lib/commands/provenance.mjs +108 -0
  12. package/lib/commands/scaffold.mjs +105 -0
  13. package/lib/commands/updateApp.mjs +211 -528
  14. package/lib/source.json +5 -0
  15. package/lib/source.mjs +63 -0
  16. package/package.json +6 -6
  17. package/scripts/frappe-bootstrap.sh +29 -3
  18. package/scripts/frappe-dev.sh +1 -2
  19. package/scripts/frappe-helpers.sh +50 -2
  20. package/scripts/frappe.sh +5 -0
  21. package/scripts/scaffold.sh +937 -0
  22. package/scripts/stamp-source.mjs +51 -0
  23. package/scripts/stamp-source.spec.ts +86 -0
  24. package/src/bin/multiplatformOne.ts +116 -226
  25. package/src/commands/addApp.ts +102 -0
  26. package/src/commands/adoptApp.spec.ts +5 -1
  27. package/src/commands/adoptApp.ts +15 -10
  28. package/src/commands/doctypeTypes.spec.ts +155 -0
  29. package/src/commands/doctypeTypes.ts +214 -0
  30. package/src/commands/init.spec.ts +66 -0
  31. package/src/commands/init.ts +201 -643
  32. package/src/commands/merge.spec.ts +81 -0
  33. package/src/commands/merge.ts +411 -0
  34. package/src/commands/pieces.spec.ts +62 -0
  35. package/src/commands/pieces.ts +340 -0
  36. package/src/commands/provenance.spec.ts +92 -0
  37. package/src/commands/provenance.ts +169 -0
  38. package/src/commands/scaffold.ts +131 -0
  39. package/src/commands/updateApp.spec.ts +540 -331
  40. package/src/commands/updateApp.ts +255 -668
  41. package/src/source.spec.ts +45 -0
  42. package/src/source.ts +88 -0
  43. package/templates/AGENTS.md +161 -0
  44. package/templates/README.md +62 -0
  45. package/templates/pieces/frappe/universal/README.md.partial +7 -32
  46. package/templates/pieces/frappe/universal/env.example.partial +3 -3
  47. package/templates/pieces/frappe-bench/universal/README.md.partial +28 -0
  48. package/templates/pieces/{frappe → frappe-bench}/universal/docker/compose.frappe.yaml +2 -2
  49. package/templates/pieces/tauri/universal/README.md.partial +7 -5
  50. package/templates/pieces/tauri/universal/apps/__NAME__/src-tauri/src/dev.rs +463 -0
  51. package/templates/pieces/tauri/universal/apps/__NAME__/src-tauri/src/lib.rs +6 -0
  52. package/templates/pieces/tauri/universal/apps/__NAME__/src-tauri/tauri.conf.json +2 -1
  53. package/templates/pieces/webext/universal/apps/__NAME__/package.json.partial +1 -0
  54. package/templates/pieces/webext/universal/apps/__NAME__/vite.config.webext.ts +6 -2
  55. package/types/bin/multiplatformOne.d.ts.map +1 -1
  56. package/types/commands/addApp.d.ts +20 -0
  57. package/types/commands/addApp.d.ts.map +1 -0
  58. package/types/commands/adoptApp.d.ts +4 -3
  59. package/types/commands/adoptApp.d.ts.map +1 -1
  60. package/types/commands/doctypeTypes.d.ts +40 -0
  61. package/types/commands/doctypeTypes.d.ts.map +1 -0
  62. package/types/commands/init.d.ts +30 -13
  63. package/types/commands/init.d.ts.map +1 -1
  64. package/types/commands/merge.d.ts +54 -0
  65. package/types/commands/merge.d.ts.map +1 -0
  66. package/types/commands/pieces.d.ts +54 -0
  67. package/types/commands/pieces.d.ts.map +1 -0
  68. package/types/commands/provenance.d.ts +52 -0
  69. package/types/commands/provenance.d.ts.map +1 -0
  70. package/types/commands/scaffold.d.ts +43 -0
  71. package/types/commands/scaffold.d.ts.map +1 -0
  72. package/types/commands/updateApp.d.ts +19 -27
  73. package/types/commands/updateApp.d.ts.map +1 -1
  74. package/types/source.d.ts +35 -0
  75. package/types/source.d.ts.map +1 -0
  76. package/lib/commands/initApp.mjs +0 -431
  77. package/lib/generateVscode.mjs +0 -184
  78. package/scripts/clone.sh +0 -17
  79. package/scripts/update.sh +0 -46
  80. package/src/commands/initApp.spec.ts +0 -720
  81. package/src/commands/initApp.ts +0 -706
  82. package/src/generateVscode.ts +0 -238
  83. package/templates/app/AGENTS.md +0 -142
  84. package/templates/app/README.md +0 -62
  85. package/templates/app/apps/__NAME__/index.html +0 -36
  86. package/templates/app/apps/__NAME__/package.json +0 -31
  87. package/templates/app/apps/__NAME__/public/fonts/inter-400.woff2 +0 -0
  88. package/templates/app/apps/__NAME__/public/fonts/inter-700.woff2 +0 -0
  89. package/templates/app/apps/__NAME__/src/App.tsx +0 -9
  90. package/templates/app/apps/__NAME__/src/main.tsx +0 -14
  91. package/templates/app/apps/__NAME__/tsconfig.json +0 -8
  92. package/templates/app/apps/__NAME__/vite.config.ts +0 -17
  93. package/templates/app/features/__NAME__/package.json +0 -34
  94. package/templates/app/features/__NAME__/src/HomeScreen.tsx +0 -70
  95. package/templates/app/features/__NAME__/src/index.ts +0 -1
  96. package/templates/app/features/__NAME__/tsconfig.json +0 -5
  97. package/templates/app/gitignore +0 -9
  98. package/templates/app/npmrc +0 -2
  99. package/templates/app/oxlintrc.json +0 -39
  100. package/templates/app/package.json +0 -38
  101. package/templates/app/packages/config/config.json +0 -4
  102. package/templates/app/packages/config/index.ts +0 -5
  103. package/templates/app/packages/config/package.json +0 -17
  104. package/templates/app/packages/config/tsconfig.json +0 -5
  105. package/templates/app/pnpm-workspace.yaml +0 -4
  106. package/templates/app/tsconfig.base.json +0 -16
  107. package/templates/universal/AGENTS.md +0 -142
  108. package/templates/universal/README.md +0 -97
  109. package/templates/universal/apps/__NAME__/app.config.js +0 -31
  110. package/templates/universal/apps/__NAME__/config/tamagui.config.ts +0 -4
  111. package/templates/universal/apps/__NAME__/package.json +0 -62
  112. package/templates/universal/apps/__NAME__/polyfills.native.ts +0 -23
  113. package/templates/universal/apps/__NAME__/polyfills.ts +0 -3
  114. package/templates/universal/apps/__NAME__/public/fonts/inter-400.woff2 +0 -0
  115. package/templates/universal/apps/__NAME__/public/fonts/inter-700.woff2 +0 -0
  116. package/templates/universal/apps/__NAME__/routes/_layout.tsx +0 -22
  117. package/templates/universal/apps/__NAME__/routes/_layout.web.tsx +0 -21
  118. package/templates/universal/apps/__NAME__/routes/about.tsx +0 -3
  119. package/templates/universal/apps/__NAME__/routes/index.tsx +0 -3
  120. package/templates/universal/apps/__NAME__/src/app.ts +0 -10
  121. package/templates/universal/apps/__NAME__/tamagui.css +0 -3
  122. package/templates/universal/apps/__NAME__/tsconfig.json +0 -8
  123. package/templates/universal/apps/__NAME__/types.d.ts +0 -9
  124. package/templates/universal/apps/__NAME__/vite.config.ts +0 -20
  125. package/templates/universal/features/__NAME__/package.json +0 -35
  126. package/templates/universal/features/__NAME__/src/AboutScreen.tsx +0 -27
  127. package/templates/universal/features/__NAME__/src/AppLayout.tsx +0 -50
  128. package/templates/universal/features/__NAME__/src/ContactForm.spec.tsx +0 -22
  129. package/templates/universal/features/__NAME__/src/ContactForm.tsx +0 -55
  130. package/templates/universal/features/__NAME__/src/HomeScreen.tsx +0 -40
  131. package/templates/universal/features/__NAME__/src/index.ts +0 -4
  132. package/templates/universal/features/__NAME__/src/screens.spec.ts +0 -12
  133. package/templates/universal/features/__NAME__/tsconfig.json +0 -5
  134. package/templates/universal/features/__NAME__/vitest.config.mjs +0 -7
  135. package/templates/universal/gitignore +0 -14
  136. package/templates/universal/npmrc +0 -3
  137. package/templates/universal/oxlintrc.json +0 -39
  138. package/templates/universal/package.json +0 -51
  139. package/templates/universal/packages/config/config.json +0 -4
  140. package/templates/universal/packages/config/index.ts +0 -5
  141. package/templates/universal/packages/config/package.json +0 -17
  142. package/templates/universal/packages/config/tsconfig.json +0 -5
  143. package/templates/universal/packages/i18n/en/common.json +0 -30
  144. package/templates/universal/packages/i18n/es/common.json +0 -30
  145. package/templates/universal/packages/i18n/i18n.d.ts +0 -16
  146. package/templates/universal/packages/i18n/index.ts +0 -26
  147. package/templates/universal/packages/i18n/package.json +0 -20
  148. package/templates/universal/packages/i18n/resources.ts +0 -13
  149. package/templates/universal/packages/i18n/tsconfig.json +0 -5
  150. package/templates/universal/packages/themes/accent.ts +0 -32
  151. package/templates/universal/packages/themes/base.ts +0 -50
  152. package/templates/universal/packages/themes/fonts.ts +0 -8
  153. package/templates/universal/packages/themes/index.ts +0 -33
  154. package/templates/universal/packages/themes/package.json +0 -23
  155. package/templates/universal/packages/themes/tamagui.config.ts +0 -14
  156. package/templates/universal/packages/themes/tsconfig.json +0 -5
  157. package/templates/universal/pnpm-workspace.yaml +0 -4
  158. package/templates/universal/tsconfig.base.json +0 -21
  159. package/templates/universal/types/env.d.ts +0 -9
  160. package/types/commands/initApp.d.ts +0 -114
  161. package/types/commands/initApp.d.ts.map +0 -1
  162. package/types/generateVscode.d.ts +0 -7
  163. package/types/generateVscode.d.ts.map +0 -1
@@ -0,0 +1,78 @@
1
+ import { PROVENANCE_FILE, cliVersion, packageJsonName, readProvenance, validateName, writeProvenance } from "./provenance.mjs";
2
+ import { finishMerge, landTree, mergeScaffolds, projectRootOf, requireCleanTree } from "./merge.mjs";
3
+ import { scaffoldToTemp } from "./scaffold.mjs";
4
+ import { rmSync } from "node:fs";
5
+ import { basename, join } from "node:path";
6
+
7
+ //#region src/commands/addApp.ts
8
+ /**
9
+ * `mpo add-app <name>`: one more product app from apps/one, merged in at the
10
+ * commit the project was scaffolded from.
11
+ *
12
+ * base = scaffold(recorded sha, recorded apps)
13
+ * theirs = scaffold(recorded sha, recorded apps + name)
14
+ * ours = HEAD
15
+ *
16
+ * Same commit on both sides, so the diff is exactly the new app and the
17
+ * root wiring that names it; the project's own changes are untouched. The
18
+ * result stays uncommitted like `mpo update`.
19
+ */
20
+ async function addApp(nameArg, options = {}) {
21
+ const name = validateName(nameArg);
22
+ const projectDir = await projectRootOf("mpo add-app");
23
+ await requireCleanTree(projectDir, "mpo add-app");
24
+ const provenance = readProvenance(projectDir);
25
+ if (!provenance) throw new Error(`${PROVENANCE_FILE} not found — mpo add-app needs a project from mpo init`);
26
+ if (!provenance.source) throw new Error(`mpo add-app needs a project scaffolded from the monorepo (a source commit in ${PROVENANCE_FILE}); adopted and template-era projects run mpo update first`);
27
+ if (provenance.apps.includes(name)) throw new Error(`apps/${name} is already a product app of this project`);
28
+ const source = options.source ?? provenance.source.url;
29
+ if (!source) throw new Error(`${PROVENANCE_FILE} records no source url — pass --source <path or url>`);
30
+ const { sha } = provenance.source;
31
+ const common = {
32
+ source,
33
+ ref: sha,
34
+ services: provenance.services,
35
+ mpoVersion: `^${provenance.cliVersion}`,
36
+ name: packageJsonName(projectDir) ?? basename(projectDir),
37
+ verbose: options.verbose
38
+ };
39
+ console.log(`\nmpo add-app ${name}: ${provenance.apps.join(", ")} + ${name} @ ${sha.slice(0, 12)}`);
40
+ const base = await scaffoldToTemp({
41
+ ...common,
42
+ apps: provenance.apps
43
+ });
44
+ const theirs = await scaffoldToTemp({
45
+ ...common,
46
+ apps: [...provenance.apps, name]
47
+ }).catch((err) => {
48
+ base.cleanup();
49
+ throw err;
50
+ });
51
+ try {
52
+ for (const dir of [base.dir, theirs.dir]) rmSync(join(dir, PROVENANCE_FILE), { force: true });
53
+ const outcome = await mergeScaffolds({
54
+ projectDir,
55
+ baseDir: base.dir,
56
+ theirsDir: theirs.dir,
57
+ baseLabel: `mpo scaffold ${sha} (${provenance.apps.join(",")})`,
58
+ theirsLabel: `mpo scaffold ${sha} (${[...provenance.apps, name].join(",")})`
59
+ });
60
+ if (!outcome.changed && !outcome.conflicts.length) {
61
+ console.log("\n✅ nothing to add (the project already carries this app)");
62
+ return;
63
+ }
64
+ await landTree(projectDir, outcome.mergedTree);
65
+ writeProvenance(projectDir, {
66
+ ...provenance,
67
+ cliVersion: cliVersion() ?? provenance.cliVersion,
68
+ apps: [...provenance.apps, name]
69
+ });
70
+ await finishMerge(projectDir, outcome, `feat: add app ${name} (mpo add-app)`, options);
71
+ } finally {
72
+ base.cleanup();
73
+ theirs.cleanup();
74
+ }
75
+ }
76
+
77
+ //#endregion
78
+ export { addApp };
@@ -1,6 +1,7 @@
1
- import { INIT_APP_PIECES, PROVENANCE_FILE, availablePieces, cliVersion, normalizePieces, packageJsonName, pieceDescription, validateName, writeProvenance } from "./initApp.mjs";
2
- import { join } from "node:path";
1
+ import { PROVENANCE_FILE, cliVersion, packageJsonName, validateName, writeProvenance } from "./provenance.mjs";
2
+ import { INIT_APP_PIECES, availablePieces, normalizePieces, pieceDescription } from "./pieces.mjs";
3
3
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
4
+ import { join } from "node:path";
4
5
  import spawn from "nano-spawn";
5
6
  import inquirer from "inquirer";
6
7
 
@@ -8,9 +9,10 @@ import inquirer from "inquirer";
8
9
  /**
9
10
  * `mpo adopt` — bring an EXISTING, never-scaffolded project under `mpo
10
11
  * update` management. Projects like this hand-copied one or more delivery
11
- * targets (webext, gnome, …) from the mpo templates and consume
12
+ * targets (webext, gnome, …) from templates/pieces and consume
12
13
  * @multiplatform.one/* from npm, but have no .mpo.json provenance, so
13
- * `mpo update` refuses to run.
14
+ * `mpo update` refuses to run. A project that wants the whole monorepo
15
+ * scaffold instead runs `mpo init --into`.
14
16
  *
15
17
  * Adoption writes provenance ONLY (template "none" + the chosen pieces +
16
18
  * the current CLI version) and seeds .updateignore — it never rewrites a
@@ -41,18 +43,18 @@ function isMpoMonorepo(projectDir) {
41
43
  }
42
44
  }
43
45
  async function resolveAdoptPieces(options) {
44
- if (options.pieces?.length) return normalizePieces(options.pieces, "none");
46
+ if (options.pieces?.length) return normalizePieces(options.pieces);
45
47
  if (!options.yes && process.stdout.isTTY) return normalizePieces((await inquirer.prompt([{
46
48
  message: "Which pieces did this project adopt? (space to toggle, enter to confirm)",
47
49
  name: "pieces",
48
50
  type: "checkbox",
49
- choices: availablePieces("none").map((piece) => ({
51
+ choices: availablePieces().map((piece) => ({
50
52
  name: `${piece} — ${pieceDescription(piece)}`,
51
53
  value: piece,
52
54
  checked: false
53
55
  })),
54
56
  validate: (selection) => selection.length > 0 || "Select at least one piece — adopting nothing is meaningless"
55
- }])).pieces, "none");
57
+ }])).pieces);
56
58
  throw new Error(`mpo adopt needs at least one piece (${INIT_APP_PIECES.map((piece) => `--${piece}`).join(", ")}) — adopting nothing is meaningless`);
57
59
  }
58
60
  async function adoptApp(options = {}) {
@@ -89,6 +91,8 @@ async function adoptApp(options = {}) {
89
91
  cliVersion: currentVersion,
90
92
  name,
91
93
  mpoVersion: `^${currentVersion}`,
94
+ apps: [],
95
+ services: [],
92
96
  pieces,
93
97
  reconciled: false
94
98
  });
@@ -0,0 +1,148 @@
1
+ //#region src/commands/doctypeTypes.ts
2
+ const generatedHeader = "// Generated by `mpo frappe types` from frappe.desk.form.load.getdoctype. Do not edit.";
3
+ const stringFieldtypes = new Set([
4
+ "Attach",
5
+ "Attach Image",
6
+ "Autocomplete",
7
+ "Barcode",
8
+ "Code",
9
+ "Color",
10
+ "Currency",
11
+ "Data",
12
+ "Date",
13
+ "Datetime",
14
+ "Dynamic Link",
15
+ "Geolocation",
16
+ "HTML Editor",
17
+ "Icon",
18
+ "JSON",
19
+ "Link",
20
+ "Long Text",
21
+ "Markdown Editor",
22
+ "Password",
23
+ "Phone",
24
+ "Read Only",
25
+ "Signature",
26
+ "Small Text",
27
+ "Text",
28
+ "Text Editor",
29
+ "Time"
30
+ ]);
31
+ const numberFieldtypes = new Set([
32
+ "Duration",
33
+ "Float",
34
+ "Int",
35
+ "Percent",
36
+ "Rating"
37
+ ]);
38
+ const valuelessFieldtypes = new Set([
39
+ "Button",
40
+ "Column Break",
41
+ "Fold",
42
+ "HTML",
43
+ "Heading",
44
+ "Image",
45
+ "Section Break",
46
+ "Tab Break"
47
+ ]);
48
+ const standardFields = [
49
+ ["name", "string"],
50
+ ["owner", "string"],
51
+ ["creation", "string"],
52
+ ["modified", "string"],
53
+ ["modified_by", "string"],
54
+ ["docstatus", "0 | 1 | 2"],
55
+ ["idx", "number"]
56
+ ];
57
+ const childFields = [
58
+ ["parent", "string"],
59
+ ["parentfield", "string"],
60
+ ["parenttype", "string"]
61
+ ];
62
+ /** "Sales Invoice Item" → "SalesInvoiceItem". */
63
+ function interfaceName(doctype) {
64
+ const name = doctype.split(/[^A-Za-z0-9]+/).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join("");
65
+ return /^[0-9]/.test(name) ? `DocType${name}` : name || "DocType";
66
+ }
67
+ function propertyKey(fieldname) {
68
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(fieldname) ? fieldname : JSON.stringify(fieldname);
69
+ }
70
+ function selectType(options) {
71
+ if (!options?.trim()) return "string";
72
+ return [...new Set(options.split("\n").map((choice) => choice.trim()))].filter((choice, index) => choice !== "" || index === 0).map((choice) => JSON.stringify(choice)).join(" | ");
73
+ }
74
+ /** The TypeScript type a field's value takes, or undefined for layout/display fields. */
75
+ function fieldTsType(field) {
76
+ const { fieldtype, options } = field;
77
+ if (valuelessFieldtypes.has(fieldtype)) return void 0;
78
+ if (stringFieldtypes.has(fieldtype)) return "string";
79
+ if (numberFieldtypes.has(fieldtype)) return "number";
80
+ if (fieldtype === "Check") return "0 | 1";
81
+ if (fieldtype === "Select") return selectType(options);
82
+ if (fieldtype === "Table" || fieldtype === "Table MultiSelect") return options ? `${interfaceName(options)}[]` : "Record<string, unknown>[]";
83
+ return "unknown";
84
+ }
85
+ function interfaceFor(meta) {
86
+ const lines = [`export interface ${interfaceName(meta.name)} {`];
87
+ lines.push(` doctype: ${JSON.stringify(meta.name)};`);
88
+ const base = meta.istable ? [...standardFields, ...childFields] : standardFields;
89
+ const taken = new Set(["doctype", ...base.map(([key]) => key)]);
90
+ for (const [key, type] of base) lines.push(` ${key}${key === "name" ? "" : "?"}: ${type};`);
91
+ for (const field of meta.fields) {
92
+ if (!field.fieldname || taken.has(field.fieldname)) continue;
93
+ const type = fieldTsType(field);
94
+ if (!type) continue;
95
+ taken.add(field.fieldname);
96
+ lines.push(` ${propertyKey(field.fieldname)}${field.reqd ? "" : "?"}: ${type};`);
97
+ }
98
+ lines.push("}");
99
+ return lines.join("\n");
100
+ }
101
+ /** One module with an interface per DocType, sorted by name so output is stable. */
102
+ function doctypesToTypeScript(docs) {
103
+ return `${[generatedHeader, ...[...new Map(docs.map((meta) => [meta.name, meta])).values()].sort((a, b) => a.name.localeCompare(b.name)).map(interfaceFor)].join("\n\n")}\n`;
104
+ }
105
+ /** The requested DocType plus its child tables, as getdoctype returns them. */
106
+ async function fetchDoctypeMeta(doctype, options) {
107
+ const url = new URL("/api/method/frappe.desk.form.load.getdoctype", options.baseURL);
108
+ url.searchParams.set("doctype", doctype);
109
+ const headers = { Accept: "application/json" };
110
+ if (options.apiKey && options.apiSecret) headers.Authorization = `token ${options.apiKey}:${options.apiSecret}`;
111
+ const response = await (options.fetch ?? fetch)(url, { headers });
112
+ if (!response.ok) throw new Error(`getdoctype ${doctype}: HTTP ${response.status}`);
113
+ const body = await response.json();
114
+ if (!Array.isArray(body.docs) || !body.docs.some((meta) => meta?.name === doctype)) throw new Error(`getdoctype ${doctype}: the response carried no meta for it`);
115
+ return body.docs;
116
+ }
117
+ async function generateDoctypeTypes(doctypes, options) {
118
+ const docs = [];
119
+ for (const doctype of doctypes) docs.push(...await fetchDoctypeMeta(doctype, options));
120
+ return doctypesToTypeScript(docs);
121
+ }
122
+ /**
123
+ * Line diff of the committed file against a fresh generation: `-` lines the
124
+ * server no longer has, `+` lines it now has. Empty means no drift.
125
+ */
126
+ function typesDrift(committed, generated) {
127
+ const before = committed.replace(/\r\n/g, "\n").split("\n");
128
+ const after = generated.split("\n");
129
+ const lcs = Array.from({ length: before.length + 1 }, () => Array.from({ length: after.length + 1 }, () => 0));
130
+ for (let i = before.length - 1; i >= 0; i -= 1) for (let j = after.length - 1; j >= 0; j -= 1) lcs[i][j] = before[i] === after[j] ? lcs[i + 1][j + 1] + 1 : Math.max(lcs[i + 1][j], lcs[i][j + 1]);
131
+ const drift = [];
132
+ let i = 0;
133
+ let j = 0;
134
+ while (i < before.length || j < after.length) if (i < before.length && j < after.length && before[i] === after[j]) {
135
+ i += 1;
136
+ j += 1;
137
+ } else if (i < before.length && (j >= after.length || lcs[i + 1][j] >= lcs[i][j + 1])) {
138
+ drift.push(`- ${before[i]}`);
139
+ i += 1;
140
+ } else {
141
+ drift.push(`+ ${after[j]}`);
142
+ j += 1;
143
+ }
144
+ return drift;
145
+ }
146
+
147
+ //#endregion
148
+ export { doctypesToTypeScript, fetchDoctypeMeta, fieldTsType, generateDoctypeTypes, generatedHeader, interfaceName, typesDrift };
@@ -1,10 +1,10 @@
1
- import fs from "node:fs/promises";
2
1
  import path from "node:path";
2
+ import fs from "node:fs/promises";
3
3
  import spawn from "nano-spawn";
4
4
  import YAML from "yaml";
5
5
  import yoctoSpinner from "yocto-spinner";
6
- import crypto from "node:crypto";
7
6
  import os from "node:os";
7
+ import crypto from "node:crypto";
8
8
 
9
9
  //#region src/commands/e2e.ts
10
10
  function sessionFileName(portOffset) {