@bettercms-ai/convert 0.15.0 → 0.16.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/CHANGELOG.md +19 -0
- package/README.md +7 -0
- package/dist/cli.js +68 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +49 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -990,7 +990,11 @@ declare const FORM_SCRIPT = "<script>\nfunction bcmsPhone(cc,raw,trunk){var d=St
|
|
|
990
990
|
* `ConversionReceipt` with an empty path arithmetic and the lane's own account under `forms`, so
|
|
991
991
|
* `submit_conversion_receipt` takes it unchanged — the server's invariant reads `paths` only.
|
|
992
992
|
*/
|
|
993
|
-
|
|
993
|
+
interface WireFormsOptions {
|
|
994
|
+
/** Form id or name → the file its `<form>` is in. The operator's answer to FORM_AMBIGUOUS. */
|
|
995
|
+
pins?: Record<string, string>;
|
|
996
|
+
}
|
|
997
|
+
declare function wireForms(briefIn: Brief, sources: SourceFile[], options_?: WireFormsOptions): Promise<{
|
|
994
998
|
files: PlanFile[];
|
|
995
999
|
receipt: ConversionReceipt;
|
|
996
1000
|
}>;
|
package/dist/index.js
CHANGED
|
@@ -6493,7 +6493,7 @@ function wire(source, candidate, form) {
|
|
|
6493
6493
|
source.appendLeft(el.inner.end - tail.length, `${tail || "\n"}${messageFor(dialect)}`);
|
|
6494
6494
|
}
|
|
6495
6495
|
}
|
|
6496
|
-
async function wireForms(briefIn, sources) {
|
|
6496
|
+
async function wireForms(briefIn, sources, options_ = {}) {
|
|
6497
6497
|
const brief = readBrief(briefIn);
|
|
6498
6498
|
const forms = brief.forms ?? [];
|
|
6499
6499
|
const candidates = [];
|
|
@@ -6531,7 +6531,34 @@ async function wireForms(briefIn, sources) {
|
|
|
6531
6531
|
}
|
|
6532
6532
|
const holds = (c, f) => f.fields.filter((field) => c.controls.has(field.key)).length;
|
|
6533
6533
|
const covers2 = (c, f) => f.fields.length > 0 && holds(c, f) === f.fields.length;
|
|
6534
|
+
const samePath = (a, b) => a.replace(/\\/g, "/").replace(/^\.\//, "") === b.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
6534
6535
|
const options = new Map(forms.map((f) => [f.id, candidates.filter((c) => covers2(c, f))]));
|
|
6536
|
+
const pinned = /* @__PURE__ */ new Map();
|
|
6537
|
+
for (const [ref, file] of Object.entries(options_.pins ?? {})) {
|
|
6538
|
+
const form = forms.find((f) => f.id === ref || f.name === ref);
|
|
6539
|
+
if (!form) throw new Error(`--pin ${ref}: the brief lists no form with that id or name.`);
|
|
6540
|
+
if (file.trim() === "") throw new Error(`--pin ${ref}: no file named. Give "<form>=<file>".`);
|
|
6541
|
+
pinned.set(form.id, file);
|
|
6542
|
+
}
|
|
6543
|
+
for (const [id, file] of pinned) {
|
|
6544
|
+
options.set(id, options.get(id).filter((c) => samePath(c.file, file)));
|
|
6545
|
+
for (const other of forms) {
|
|
6546
|
+
const its = pinned.get(other.id);
|
|
6547
|
+
if (other.id === id || its !== void 0 && samePath(its, file)) continue;
|
|
6548
|
+
options.set(other.id, options.get(other.id).filter((c) => !samePath(c.file, file)));
|
|
6549
|
+
}
|
|
6550
|
+
}
|
|
6551
|
+
const beat = /* @__PURE__ */ new Map();
|
|
6552
|
+
for (const c of candidates) {
|
|
6553
|
+
const claimants = forms.filter((f) => options.get(f.id).includes(c));
|
|
6554
|
+
if (claimants.length < 2) continue;
|
|
6555
|
+
const top = Math.max(...claimants.map((f) => holds(c, f)));
|
|
6556
|
+
const winners = claimants.filter((f) => holds(c, f) === top);
|
|
6557
|
+
if (winners.length !== 1) continue;
|
|
6558
|
+
const rivals = claimants.filter((f) => f !== winners[0]);
|
|
6559
|
+
for (const f of rivals) options.set(f.id, options.get(f.id).filter((x) => x !== c));
|
|
6560
|
+
beat.set(c, { winner: winners[0], rivals });
|
|
6561
|
+
}
|
|
6535
6562
|
const wanted = /* @__PURE__ */ new Map();
|
|
6536
6563
|
for (const list of options.values()) for (const c of list) wanted.set(c, (wanted.get(c) ?? 0) + 1);
|
|
6537
6564
|
const matched = [];
|
|
@@ -6542,7 +6569,12 @@ async function wireForms(briefIn, sources) {
|
|
|
6542
6569
|
for (const form of forms) {
|
|
6543
6570
|
const list = options.get(form.id) ?? [];
|
|
6544
6571
|
if (list.length > 1) {
|
|
6545
|
-
pend2(
|
|
6572
|
+
pend2(
|
|
6573
|
+
form,
|
|
6574
|
+
"FORM_AMBIGUOUS",
|
|
6575
|
+
void 0,
|
|
6576
|
+
`${list.length} forms post every field of "${form.name}": ${list.map((c) => c.file).join(", ")}.${pinned.has(form.id) ? " The pin names that file, and it holds more than one." : ` Name the right one with --pin "${form.name}=<file>".`}`
|
|
6577
|
+
);
|
|
6546
6578
|
continue;
|
|
6547
6579
|
}
|
|
6548
6580
|
if (list.length === 1 && (wanted.get(list[0]) ?? 0) > 1) {
|
|
@@ -6551,7 +6583,13 @@ async function wireForms(briefIn, sources) {
|
|
|
6551
6583
|
}
|
|
6552
6584
|
if (list.length === 0) {
|
|
6553
6585
|
const [best] = candidates.map((c) => ({ c, n: holds(c, form) })).filter((x) => x.n > 0).sort((a, b) => b.n - a.n);
|
|
6554
|
-
|
|
6586
|
+
const pin = pinned.get(form.id);
|
|
6587
|
+
if (pin) {
|
|
6588
|
+
pend2(form, "FORM_NOT_IN_SOURCE", pin, `the pinned file has no <form> posting ${form.fields.map((f) => f.key).join(", ")}.`);
|
|
6589
|
+
} else if (best && best.n === form.fields.length) {
|
|
6590
|
+
const owner = forms.find((f) => f !== form && options.get(f.id).includes(best.c));
|
|
6591
|
+
pend2(form, "FORM_NOT_IN_SOURCE", best.c.file, `the only <form> posting ${form.fields.map((f) => f.key).join(", ")} is "${owner?.name ?? "another form"}"'s.`);
|
|
6592
|
+
} else if (best) {
|
|
6555
6593
|
const missing = form.fields.filter((f) => !best.c.controls.has(f.key)).map((f) => f.key);
|
|
6556
6594
|
pend2(form, "FIELD_UNMATCHED", best.c.file, `no control posts ${missing.join(", ")} in the <form> that posts its other fields.`);
|
|
6557
6595
|
} else if (unreadable[0]) {
|
|
@@ -6599,6 +6637,14 @@ ${indent}${scriptFor(last.candidate.dialect)}`);
|
|
|
6599
6637
|
files.push({ path, operation: "modify", content: source.toString() });
|
|
6600
6638
|
}
|
|
6601
6639
|
const notes = onSite.filter(({ form }) => form.status === "draft").map(({ form }) => `publish form ${form.name} in the Forms tab \u2014 a draft form rejects every submission with 403.`);
|
|
6640
|
+
for (const { form, candidate } of matched) {
|
|
6641
|
+
const tie = beat.get(candidate);
|
|
6642
|
+
if (tie?.winner === form) {
|
|
6643
|
+
notes.push(
|
|
6644
|
+
`form ${form.name}: ${candidate.file} posts every field of ${tie.rivals.map((f) => f.name).join(", ")} too, and more of ${form.name}'s, so it went to ${form.name} \u2014 name the file yourself with --pin "<form>=<file>" if that is wrong.`
|
|
6645
|
+
);
|
|
6646
|
+
}
|
|
6647
|
+
}
|
|
6602
6648
|
for (const { form, candidate } of matched) {
|
|
6603
6649
|
const keys = form.fields.filter((field) => candidate.foreign.has(candidate.controls.get(field.key))).map((field) => field.key);
|
|
6604
6650
|
if (keys.length > 0) {
|