@ai-matrx/records 0.76.10 → 0.76.11
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 +6 -0
- package/dist/templates/index.cjs +116 -12
- package/dist/templates/index.cjs.map +1 -1
- package/dist/templates/index.d.cts +15 -2
- package/dist/templates/index.d.ts +15 -2
- package/dist/templates/index.js +116 -12
- package/dist/templates/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -823,7 +823,7 @@ declare function reusesOf(plan: DescribePlan): Array<{
|
|
|
823
823
|
* S5 step 1 and assembly. `doors` and `views` may be missing (a failed step): the views fall back to the default, the
|
|
824
824
|
* forms stay empty (a missing front door is for the check to say, never for code to invent).
|
|
825
825
|
*/
|
|
826
|
-
declare function mergeDescribeParts(plan: DescribePlan, tables: TablesPart, doors: DoorsPart | null | undefined, views: ViewsPart | null | undefined, existingTables?: ExistingTable[]): TemplateSpec;
|
|
826
|
+
declare function mergeDescribeParts(plan: DescribePlan, tables: TablesPart, doors: DoorsPart | null | undefined, views: ViewsPart | null | undefined, existingTables?: ExistingTable[], notes?: string[]): TemplateSpec;
|
|
827
827
|
|
|
828
828
|
interface AutofixResult {
|
|
829
829
|
spec: TemplateSpec;
|
|
@@ -915,4 +915,17 @@ declare const VIEW_SHAPE_FACTS: {
|
|
|
915
915
|
/** The store's closed lists, as the mandate's `vocabulary` value — read from the package, never typed by a caller. */
|
|
916
916
|
declare function describeVocabulary(): Record<string, unknown>;
|
|
917
917
|
|
|
918
|
-
|
|
918
|
+
/** The content words of a key or label, stemmed, as a sorted unique list. */
|
|
919
|
+
declare function meaningWords(text: string): string[];
|
|
920
|
+
/** Two fields of one table mean the same when their kinds agree and their key words, or label words, are the same set. */
|
|
921
|
+
declare function sameMeaning(a: {
|
|
922
|
+
key: string;
|
|
923
|
+
label?: string;
|
|
924
|
+
parityType: string;
|
|
925
|
+
}, b: {
|
|
926
|
+
key: string;
|
|
927
|
+
label?: string;
|
|
928
|
+
parityType: string;
|
|
929
|
+
}): boolean;
|
|
930
|
+
|
|
931
|
+
export { AgentSnapshotRow, type AggregateOutcome, type ApplyResult, type AutofixResult, BAD_OUTCOME, BOOKING_AVAILABILITY_FACTS, type CapabilityStatus, CapabilityWord, DESIGNATED_REAL_NUMBERS, type DerivedUseCase, type DescribeCheckResult, type DescribeCtx, type DescribePlan, type DescribePlanDoor, type DescribePlanTable, type DescribeProblem, type DescribeResult, type DoorsPart, type ExistingTable, FORMULA_FUNCTIONS, type FieldRequest, GALLERY_ONLY, GOOD_OUTCOME, INSTALL_PLAN_VERSION, IndustryGroup, MAIN_TABLE_ROWS_MAX, MAIN_TABLE_ROWS_MIN, PUBLIC_MAIL_DOMAINS, RECORDS_UI_CALENDAR_MODES, RECORDS_UI_FIELD_SURFACES, RECORDS_UI_VIEW_LAYOUTS, type RepairAnswer, type RepairPatch, type RepairSlice, SERVER_DATE_GRAMMAR, type SeedRow, SharedBlockId, TABLES_MAX, TABLES_MIN, TEMPLATE_PLAN_DOORS, TIME_BOUND_STAGE, TITLE_DISTINCT_SHARE, type TablesPart, TeachesFeature, TemplateAgent, type TemplateAgentBinding, type TemplateAgentCopyRequest, type TemplateAssetRow, type TemplateAttachment, TemplateAutomation, TemplateBooking, TemplateBookingWindow, type TemplateCard, TemplateChecklist, TemplateDashboard, type TemplateDeclaration, TemplateDigest, TemplateDimensions, TemplateDocument, type TemplateDoorAnswer, type TemplateDoorRun, TemplateDrillDown, type TemplateFamilyDoor, TemplateField, TemplateFootprint, TemplateForm, type TemplateHostExtras, type TemplateInstallPlan, TemplateJob, type TemplateMadeKind, TemplateMergeBinding, TemplateNotification, type TemplatePlanAgent, type TemplatePlanBinding, type TemplatePlanDoor, type TemplatePlanExtraAgent, type TemplatePlanStep, TemplatePortal, TemplateRowAction, TemplateSpec, type TemplateSpecWithAttachments, TemplateStageRules, type TemplateStatus, TemplateTable, TemplateTryIt, type TemplateUpgradeAnswer, type TemplateUpgradeHint, type TemplateUpgradeRun, type TemplateVerdict, TemplateView, TemplateViewKind, TemplateWorkflow, type TrimResult, VIEW_SHAPE_FACTS, type ValidateOptions, type ViewsPart, agentCopyRequest, allTables, anyOfWhere, applyRepairPatches, assertTemplate, attachmentProblems, autofixDescribeSpec, automationHeld, blockTable, bookingArgs, bookingColumnsProblem, capabilitiesUsed, capabilityStatus, checklistSpec, choiceDisplayFormat, compileHostExtras, compileInstallPlan, dashboardBlocks, deferredRelationFields, deferredRelations, derivedFieldSpec, describeCheck, describeSpec, describeVocabulary, digestSpec, documentBody, domainsIn, drillDownDashboard, evalFormula, extrasProblems, fallbackView, foreignTextProblems, formArgs, formulaFieldSpec, formulaOrder, formulaRefs, foundationTokens, isSelfDeclaredField, isTimedField, lastResortTrim, meaningWords, mergeDescribeParts, normalizeForPhones, notificationParts, numberText, numberedStreet, packageDescribeResult, parseAt, parseFormula, parseNumeric, pathOfAt, peopleOf, phonesOutsideFictionalRange, planTables, portalArgs, relationFieldSpec, relationPatches, relativeHasTime, repairSlices, requiredCircleProblem, resolveDateToken, resolvePath, resolveRelativeDates, reusesOf, rowActionList, rowLevels, rowsFor, ruleBreak, runTemplateDoor, sameMeaning, selfDeclaredFieldSpec, sensitivityFloorOf, shellsPayload, stageFieldOf, stageRulesSpec, storeAggregate, storeFilterMatches, storeText, tableShellPayload, templateCard, templateDeclaration, templateText, templateUpgradeHint, timedFieldSpec, toUseCase, unwrap, upgradeTemplateInstall, useCaseProblemsOf, validateTemplate, viewMatches, viewSpec, zoneOffset };
|
|
@@ -823,7 +823,7 @@ declare function reusesOf(plan: DescribePlan): Array<{
|
|
|
823
823
|
* S5 step 1 and assembly. `doors` and `views` may be missing (a failed step): the views fall back to the default, the
|
|
824
824
|
* forms stay empty (a missing front door is for the check to say, never for code to invent).
|
|
825
825
|
*/
|
|
826
|
-
declare function mergeDescribeParts(plan: DescribePlan, tables: TablesPart, doors: DoorsPart | null | undefined, views: ViewsPart | null | undefined, existingTables?: ExistingTable[]): TemplateSpec;
|
|
826
|
+
declare function mergeDescribeParts(plan: DescribePlan, tables: TablesPart, doors: DoorsPart | null | undefined, views: ViewsPart | null | undefined, existingTables?: ExistingTable[], notes?: string[]): TemplateSpec;
|
|
827
827
|
|
|
828
828
|
interface AutofixResult {
|
|
829
829
|
spec: TemplateSpec;
|
|
@@ -915,4 +915,17 @@ declare const VIEW_SHAPE_FACTS: {
|
|
|
915
915
|
/** The store's closed lists, as the mandate's `vocabulary` value — read from the package, never typed by a caller. */
|
|
916
916
|
declare function describeVocabulary(): Record<string, unknown>;
|
|
917
917
|
|
|
918
|
-
|
|
918
|
+
/** The content words of a key or label, stemmed, as a sorted unique list. */
|
|
919
|
+
declare function meaningWords(text: string): string[];
|
|
920
|
+
/** Two fields of one table mean the same when their kinds agree and their key words, or label words, are the same set. */
|
|
921
|
+
declare function sameMeaning(a: {
|
|
922
|
+
key: string;
|
|
923
|
+
label?: string;
|
|
924
|
+
parityType: string;
|
|
925
|
+
}, b: {
|
|
926
|
+
key: string;
|
|
927
|
+
label?: string;
|
|
928
|
+
parityType: string;
|
|
929
|
+
}): boolean;
|
|
930
|
+
|
|
931
|
+
export { AgentSnapshotRow, type AggregateOutcome, type ApplyResult, type AutofixResult, BAD_OUTCOME, BOOKING_AVAILABILITY_FACTS, type CapabilityStatus, CapabilityWord, DESIGNATED_REAL_NUMBERS, type DerivedUseCase, type DescribeCheckResult, type DescribeCtx, type DescribePlan, type DescribePlanDoor, type DescribePlanTable, type DescribeProblem, type DescribeResult, type DoorsPart, type ExistingTable, FORMULA_FUNCTIONS, type FieldRequest, GALLERY_ONLY, GOOD_OUTCOME, INSTALL_PLAN_VERSION, IndustryGroup, MAIN_TABLE_ROWS_MAX, MAIN_TABLE_ROWS_MIN, PUBLIC_MAIL_DOMAINS, RECORDS_UI_CALENDAR_MODES, RECORDS_UI_FIELD_SURFACES, RECORDS_UI_VIEW_LAYOUTS, type RepairAnswer, type RepairPatch, type RepairSlice, SERVER_DATE_GRAMMAR, type SeedRow, SharedBlockId, TABLES_MAX, TABLES_MIN, TEMPLATE_PLAN_DOORS, TIME_BOUND_STAGE, TITLE_DISTINCT_SHARE, type TablesPart, TeachesFeature, TemplateAgent, type TemplateAgentBinding, type TemplateAgentCopyRequest, type TemplateAssetRow, type TemplateAttachment, TemplateAutomation, TemplateBooking, TemplateBookingWindow, type TemplateCard, TemplateChecklist, TemplateDashboard, type TemplateDeclaration, TemplateDigest, TemplateDimensions, TemplateDocument, type TemplateDoorAnswer, type TemplateDoorRun, TemplateDrillDown, type TemplateFamilyDoor, TemplateField, TemplateFootprint, TemplateForm, type TemplateHostExtras, type TemplateInstallPlan, TemplateJob, type TemplateMadeKind, TemplateMergeBinding, TemplateNotification, type TemplatePlanAgent, type TemplatePlanBinding, type TemplatePlanDoor, type TemplatePlanExtraAgent, type TemplatePlanStep, TemplatePortal, TemplateRowAction, TemplateSpec, type TemplateSpecWithAttachments, TemplateStageRules, type TemplateStatus, TemplateTable, TemplateTryIt, type TemplateUpgradeAnswer, type TemplateUpgradeHint, type TemplateUpgradeRun, type TemplateVerdict, TemplateView, TemplateViewKind, TemplateWorkflow, type TrimResult, VIEW_SHAPE_FACTS, type ValidateOptions, type ViewsPart, agentCopyRequest, allTables, anyOfWhere, applyRepairPatches, assertTemplate, attachmentProblems, autofixDescribeSpec, automationHeld, blockTable, bookingArgs, bookingColumnsProblem, capabilitiesUsed, capabilityStatus, checklistSpec, choiceDisplayFormat, compileHostExtras, compileInstallPlan, dashboardBlocks, deferredRelationFields, deferredRelations, derivedFieldSpec, describeCheck, describeSpec, describeVocabulary, digestSpec, documentBody, domainsIn, drillDownDashboard, evalFormula, extrasProblems, fallbackView, foreignTextProblems, formArgs, formulaFieldSpec, formulaOrder, formulaRefs, foundationTokens, isSelfDeclaredField, isTimedField, lastResortTrim, meaningWords, mergeDescribeParts, normalizeForPhones, notificationParts, numberText, numberedStreet, packageDescribeResult, parseAt, parseFormula, parseNumeric, pathOfAt, peopleOf, phonesOutsideFictionalRange, planTables, portalArgs, relationFieldSpec, relationPatches, relativeHasTime, repairSlices, requiredCircleProblem, resolveDateToken, resolvePath, resolveRelativeDates, reusesOf, rowActionList, rowLevels, rowsFor, ruleBreak, runTemplateDoor, sameMeaning, selfDeclaredFieldSpec, sensitivityFloorOf, shellsPayload, stageFieldOf, stageRulesSpec, storeAggregate, storeFilterMatches, storeText, tableShellPayload, templateCard, templateDeclaration, templateText, templateUpgradeHint, timedFieldSpec, toUseCase, unwrap, upgradeTemplateInstall, useCaseProblemsOf, validateTemplate, viewMatches, viewSpec, zoneOffset };
|
package/dist/templates/index.js
CHANGED
|
@@ -4366,8 +4366,8 @@ function validateTemplate(raw, options = {}) {
|
|
|
4366
4366
|
for (const k of rule.filled ?? []) if (!filled(val(k))) say(at, `"${row.key}" is ${st}, so "${fieldOf(table, k)?.label ?? k}" is filled; it is empty.`);
|
|
4367
4367
|
for (const [k, v] of Object.entries(rule.equals ?? {})) {
|
|
4368
4368
|
const got = val(k) ?? null;
|
|
4369
|
-
const
|
|
4370
|
-
if (!
|
|
4369
|
+
const same2 = sameJson(got, v) || typeof v === "number" && parseNumeric(storeText(got) ?? "") !== null && qCmp(parseNumeric(storeText(got) ?? ""), parseNumeric(String(v))) === 0;
|
|
4370
|
+
if (!same2) say(at, `"${row.key}" is ${st}, so "${fieldOf(table, k)?.label ?? k}" is ${JSON.stringify(v)}; it is ${JSON.stringify(got)}.`);
|
|
4371
4371
|
}
|
|
4372
4372
|
}
|
|
4373
4373
|
};
|
|
@@ -5704,7 +5704,7 @@ ${e.body}`.matchAll(MERGE_TOKEN)].map((m) => m[1]));
|
|
|
5704
5704
|
}
|
|
5705
5705
|
}
|
|
5706
5706
|
const rows = t.rows;
|
|
5707
|
-
const
|
|
5707
|
+
const same2 = (x, y) => {
|
|
5708
5708
|
const a = storeText(x);
|
|
5709
5709
|
const b = storeText(y);
|
|
5710
5710
|
if (a === null || b === null) return false;
|
|
@@ -5722,7 +5722,7 @@ ${e.body}`.matchAll(MERGE_TOKEN)].map((m) => m[1]));
|
|
|
5722
5722
|
};
|
|
5723
5723
|
for (const sf of t.fields.filter((ff) => !isComputed(ff) && !isRelation(ff) && ff.key !== l.key)) {
|
|
5724
5724
|
const have = rows.filter((r) => filled(r.values?.[sf.key]));
|
|
5725
|
-
if (have.length >= 3 && have.every((r) =>
|
|
5725
|
+
if (have.length >= 3 && have.every((r) => same2(r.values?.[sf.key], far(r)))) {
|
|
5726
5726
|
say(`tables (${t.token}).fields (${sf.key})`, `"${sf.label}" repeats "${l.label}" on every row; drop it and use the lookup, which follows the linked record.`);
|
|
5727
5727
|
}
|
|
5728
5728
|
}
|
|
@@ -6407,6 +6407,33 @@ function describeField(f, table) {
|
|
|
6407
6407
|
};
|
|
6408
6408
|
}
|
|
6409
6409
|
|
|
6410
|
+
// src/templates/describe/meaning.ts
|
|
6411
|
+
var FILLER = /* @__PURE__ */ new Set(["of", "the", "a", "an", "for", "to", "in", "on", "and", "or", "your", "their", "is", "are", "was", "when", "what", "which", "with", "my", "our"]);
|
|
6412
|
+
function stemWord(w) {
|
|
6413
|
+
let s = w.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
6414
|
+
if (s.length > 4 && s.endsWith("ies")) s = s.slice(0, -3) + "y";
|
|
6415
|
+
else if (s.length > 4 && s.endsWith("es") && /(ch|sh|x|ss|s)es$/.test(s)) s = s.slice(0, -2);
|
|
6416
|
+
else if (s.length > 3 && s.endsWith("s") && !s.endsWith("ss")) s = s.slice(0, -1);
|
|
6417
|
+
if (s.length > 6 && s.endsWith("ation")) s = s.slice(0, -5);
|
|
6418
|
+
else if (s.length > 6 && s.endsWith("ion")) s = s.slice(0, -3);
|
|
6419
|
+
if (s.length > 4 && (s.endsWith("y") || s.endsWith("e"))) s = s.slice(0, -1);
|
|
6420
|
+
return s;
|
|
6421
|
+
}
|
|
6422
|
+
function meaningWords(text) {
|
|
6423
|
+
const words = text.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean).map((w) => w.toLowerCase()).filter((w) => !FILLER.has(w)).map(stemWord).filter(Boolean);
|
|
6424
|
+
return [...new Set(words)].sort();
|
|
6425
|
+
}
|
|
6426
|
+
var same = (a, b) => a.length > 0 && a.length === b.length && a.every((w, i) => w === b[i]);
|
|
6427
|
+
function sameMeaning(a, b) {
|
|
6428
|
+
if (a.parityType !== b.parityType) return false;
|
|
6429
|
+
const ka = meaningWords(a.key);
|
|
6430
|
+
const kb = meaningWords(b.key);
|
|
6431
|
+
if (same(ka, kb)) return true;
|
|
6432
|
+
const la = a.label ? meaningWords(a.label) : [];
|
|
6433
|
+
const lb = b.label ? meaningWords(b.label) : [];
|
|
6434
|
+
return same(la, lb);
|
|
6435
|
+
}
|
|
6436
|
+
|
|
6410
6437
|
// src/templates/describe/merge.ts
|
|
6411
6438
|
var clone = (v) => v === void 0 ? v : JSON.parse(JSON.stringify(v));
|
|
6412
6439
|
var labelOfKey = (key) => {
|
|
@@ -6439,7 +6466,7 @@ function ensureSkeleton(t, p) {
|
|
|
6439
6466
|
t.fields.push({ key: "status", label: "Status", parityType: "select", required: true, choices: [...p.status.choices], default: p.status.default });
|
|
6440
6467
|
}
|
|
6441
6468
|
if (p.slot && !field(t, "slot")) t.fields.push({ key: "slot", label: "Visit time", parityType: "datetime", format: "datetime", required: true });
|
|
6442
|
-
for (const l of p.links) {
|
|
6469
|
+
for (const l of p.links ?? []) {
|
|
6443
6470
|
if (field(t, l.key)) continue;
|
|
6444
6471
|
t.fields.push({ key: l.key, label: l.label, parityType: "relation", relationTarget: l.target, ...l.many ? { multi: true } : { relationMax: 1 } });
|
|
6445
6472
|
}
|
|
@@ -6448,7 +6475,7 @@ function addExistingKeys(t, p, existing) {
|
|
|
6448
6475
|
if (!p.reuse) return;
|
|
6449
6476
|
const e = existing.find((x) => x.id === p.reuse.existing_table_id);
|
|
6450
6477
|
if (!e) return;
|
|
6451
|
-
for (const key of p.reuse.keepKeys) {
|
|
6478
|
+
for (const key of p.reuse.keepKeys ?? []) {
|
|
6452
6479
|
if (field(t, key)) continue;
|
|
6453
6480
|
const ef = e.fields.find((f) => f.key === key);
|
|
6454
6481
|
if (!ef) continue;
|
|
@@ -6456,7 +6483,9 @@ function addExistingKeys(t, p, existing) {
|
|
|
6456
6483
|
}
|
|
6457
6484
|
}
|
|
6458
6485
|
function addRequest(t, r) {
|
|
6459
|
-
if (field(t, r.key)) return;
|
|
6486
|
+
if (field(t, r.key)) return void 0;
|
|
6487
|
+
const twin = t.fields.find((f2) => sameMeaning(f2, r));
|
|
6488
|
+
if (twin) return twin.key;
|
|
6460
6489
|
const f = { key: r.key, label: r.label, parityType: r.parityType };
|
|
6461
6490
|
if (r.choices?.length) f.choices = [...r.choices];
|
|
6462
6491
|
if (r.askOfSubject) f.askOfSubject = true;
|
|
@@ -6464,6 +6493,7 @@ function addRequest(t, r) {
|
|
|
6464
6493
|
if (r.required) f.required = true;
|
|
6465
6494
|
if (r.help) f.help = r.help;
|
|
6466
6495
|
t.fields.push(f);
|
|
6496
|
+
return void 0;
|
|
6467
6497
|
}
|
|
6468
6498
|
var CHOICE = ["select", "multi_select", "list"];
|
|
6469
6499
|
var STAGE_KEY = /(^|_)(status|stage)$/;
|
|
@@ -6525,7 +6555,7 @@ function sharedBlocksFor(tables) {
|
|
|
6525
6555
|
function reusesOf(plan) {
|
|
6526
6556
|
return plan.tables.filter((t) => t.reuse).map((t) => ({ token: t.token, existing_table_id: t.reuse.existing_table_id }));
|
|
6527
6557
|
}
|
|
6528
|
-
function mergeDescribeParts(plan, tables, doors, views, existingTables = []) {
|
|
6558
|
+
function mergeDescribeParts(plan, tables, doors, views, existingTables = [], notes = []) {
|
|
6529
6559
|
const out = clone(Array.isArray(tables?.tables) ? tables.tables : []);
|
|
6530
6560
|
const byToken = new Map(out.map((t) => [t.token, t]));
|
|
6531
6561
|
for (const p of plan.tables) {
|
|
@@ -6538,10 +6568,25 @@ function mergeDescribeParts(plan, tables, doors, views, existingTables = []) {
|
|
|
6538
6568
|
ensureSkeleton(t, p);
|
|
6539
6569
|
addExistingKeys(t, p, existingTables);
|
|
6540
6570
|
}
|
|
6571
|
+
const repoint = /* @__PURE__ */ new Map();
|
|
6541
6572
|
for (const r of doors?.fieldRequests ?? []) {
|
|
6542
6573
|
const t = byToken.get(r.table);
|
|
6543
|
-
if (t)
|
|
6544
|
-
|
|
6574
|
+
if (!t) continue;
|
|
6575
|
+
const twin = addRequest(t, r);
|
|
6576
|
+
if (!twin) continue;
|
|
6577
|
+
if (!repoint.has(r.table)) repoint.set(r.table, /* @__PURE__ */ new Map());
|
|
6578
|
+
repoint.get(r.table).set(r.key, twin);
|
|
6579
|
+
notes.push(`tables.${r.table}.fields.${r.key}: dropped, it means the same as "${twin}" (questions now ask "${twin}")`);
|
|
6580
|
+
}
|
|
6581
|
+
const forms = clone(doors?.forms ?? []);
|
|
6582
|
+
const bookingOut = doors?.booking ? clone(doors.booking) : void 0;
|
|
6583
|
+
const repointQuestions = (table, qs) => {
|
|
6584
|
+
const m = repoint.get(table);
|
|
6585
|
+
if (!m || !Array.isArray(qs)) return;
|
|
6586
|
+
for (const q2 of qs) if (m.has(q2.fieldKey)) q2.fieldKey = m.get(q2.fieldKey);
|
|
6587
|
+
};
|
|
6588
|
+
for (const f of forms) repointQuestions(f.table, f.fields);
|
|
6589
|
+
if (bookingOut) repointQuestions(bookingOut.table, bookingOut.questions);
|
|
6545
6590
|
const spec = {
|
|
6546
6591
|
specVersion: 1,
|
|
6547
6592
|
version: 1,
|
|
@@ -6558,9 +6603,9 @@ function mergeDescribeParts(plan, tables, doors, views, existingTables = []) {
|
|
|
6558
6603
|
sharedBlocks: sharedBlocksFor(out),
|
|
6559
6604
|
relationships: clone(tables?.relationships ?? []),
|
|
6560
6605
|
views: mergeViews(plan, views, byToken),
|
|
6561
|
-
forms
|
|
6606
|
+
forms,
|
|
6562
6607
|
dimensions: [],
|
|
6563
|
-
extras:
|
|
6608
|
+
extras: bookingOut ? [bookingOut] : []
|
|
6564
6609
|
};
|
|
6565
6610
|
return spec;
|
|
6566
6611
|
}
|
|
@@ -6576,6 +6621,62 @@ var GOOD_ORDER = ["green", "teal", "blue", "violet", "slate", "amber", "orange",
|
|
|
6576
6621
|
var BAD_ORDER = ["red", "orange", "gray", "slate", "brown", "pink", "amber", "yellow", "violet", "blue", "teal"];
|
|
6577
6622
|
var DISTINCT_UP_TO = 7;
|
|
6578
6623
|
var lower = (s) => s.trim().toLowerCase();
|
|
6624
|
+
function linkedTables(spec, token) {
|
|
6625
|
+
const out = [];
|
|
6626
|
+
const add = (x) => {
|
|
6627
|
+
const t = spec.tables.find((y) => y.token === x);
|
|
6628
|
+
if (t && t.token !== token && !out.includes(t)) out.push(t);
|
|
6629
|
+
};
|
|
6630
|
+
const own = spec.tables.find((t) => t.token === token);
|
|
6631
|
+
for (const f of own?.fields ?? []) if (f.parityType === "relation" && f.relationTarget) add(f.relationTarget);
|
|
6632
|
+
for (const t of spec.tables) if (t.fields.some((f) => f.parityType === "relation" && f.relationTarget === token)) add(t.token);
|
|
6633
|
+
for (const r of spec.relationships ?? []) {
|
|
6634
|
+
if (r.fromTable === token) add(r.toTable);
|
|
6635
|
+
if (r.toTable === token) add(r.fromTable);
|
|
6636
|
+
}
|
|
6637
|
+
return out;
|
|
6638
|
+
}
|
|
6639
|
+
function fixMissingQuestionFields(spec, fixes) {
|
|
6640
|
+
const doors = [];
|
|
6641
|
+
for (const form of spec.forms) doors.push({ where: `forms.${form.token}`, table: form.table, questions: form.fields ?? [], set: (q2) => form.fields = q2 });
|
|
6642
|
+
for (const e of spec.extras ?? []) {
|
|
6643
|
+
if (e.kind !== "booking") continue;
|
|
6644
|
+
const b = e;
|
|
6645
|
+
doors.push({ where: `extras.${b.token}`, table: b.table, questions: b.questions ?? [], set: (q2) => b.questions = q2 });
|
|
6646
|
+
}
|
|
6647
|
+
for (const d of doors) {
|
|
6648
|
+
const t = spec.tables.find((x) => x.token === d.table);
|
|
6649
|
+
if (!t) continue;
|
|
6650
|
+
const kept = [];
|
|
6651
|
+
for (const q2 of d.questions) {
|
|
6652
|
+
if (t.fields.some((f) => f.key === q2.fieldKey)) {
|
|
6653
|
+
kept.push(q2);
|
|
6654
|
+
continue;
|
|
6655
|
+
}
|
|
6656
|
+
const source = linkedTables(spec, t.token).find((o) => o.fields.some((f) => f.key === q2.fieldKey));
|
|
6657
|
+
if (source) {
|
|
6658
|
+
const copy = clone2(source.fields.find((f) => f.key === q2.fieldKey));
|
|
6659
|
+
delete copy.required;
|
|
6660
|
+
delete copy.unique;
|
|
6661
|
+
const isDate2 = copy.parityType === "datetime";
|
|
6662
|
+
if (isDate2 && !copy.absoluteDates && /birth|dob|born/i.test(`${copy.key} ${copy.label}`)) {
|
|
6663
|
+
copy.absoluteDates = true;
|
|
6664
|
+
copy.historicalReason = copy.historicalReason?.trim() || "date of birth";
|
|
6665
|
+
}
|
|
6666
|
+
const hasSchedule = t.fields.some((f) => f.parityType === "datetime" && !f.absoluteDates);
|
|
6667
|
+
t.fields.push(copy);
|
|
6668
|
+
if (isDate2 && !copy.absoluteDates && hasSchedule && !t.before?.length && !t.datesIndependent?.trim()) {
|
|
6669
|
+
t.datesIndependent = `"${copy.label}" is the linked ${source.token}'s own date; it does not depend on the table's other dates.`;
|
|
6670
|
+
}
|
|
6671
|
+
fixes.push(`tables.${t.token}.fields.${q2.fieldKey}: copied from linked table "${source.token}" (${d.where} asks it)`);
|
|
6672
|
+
kept.push(q2);
|
|
6673
|
+
} else if (!q2.required) {
|
|
6674
|
+
fixes.push(`${d.where}.fields.${q2.fieldKey}: question dropped ("${t.token}" declares no such field and no linked table has it)`);
|
|
6675
|
+
} else kept.push(q2);
|
|
6676
|
+
}
|
|
6677
|
+
if (kept.length !== d.questions.length) d.set(kept);
|
|
6678
|
+
}
|
|
6679
|
+
}
|
|
6579
6680
|
function raiseSensitivity(spec, fixes) {
|
|
6580
6681
|
for (const t of spec.tables) {
|
|
6581
6682
|
for (const f of t.fields) {
|
|
@@ -6830,6 +6931,7 @@ function switchJob(spec, fixes) {
|
|
|
6830
6931
|
function autofixDescribeSpec(input, ctx = {}) {
|
|
6831
6932
|
const spec = describeSpec(clone2(input));
|
|
6832
6933
|
const autoFixes = [];
|
|
6934
|
+
fixMissingQuestionFields(spec, autoFixes);
|
|
6833
6935
|
raiseSensitivity(spec, autoFixes);
|
|
6834
6936
|
flagAskedOfSubject(spec, ctx, autoFixes);
|
|
6835
6937
|
fixBooking(spec, autoFixes);
|
|
@@ -7163,6 +7265,7 @@ export {
|
|
|
7163
7265
|
isSelfDeclaredField,
|
|
7164
7266
|
isTimedField,
|
|
7165
7267
|
lastResortTrim,
|
|
7268
|
+
meaningWords,
|
|
7166
7269
|
membersAddRowsSteps,
|
|
7167
7270
|
mergeDescribeParts,
|
|
7168
7271
|
normalizeForPhones,
|
|
@@ -7192,6 +7295,7 @@ export {
|
|
|
7192
7295
|
rowsFor,
|
|
7193
7296
|
ruleBreak,
|
|
7194
7297
|
runTemplateDoor,
|
|
7298
|
+
sameMeaning,
|
|
7195
7299
|
selfDeclaredFieldSpec,
|
|
7196
7300
|
sensitivityFloorOf,
|
|
7197
7301
|
shellsPayload,
|