@casys/mcp-erpnext 3.1.0-beta.7 → 3.1.0-beta.9
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/LICENSE +21 -0
- package/README.md +57 -14
- package/THIRD_PARTY_NOTICES.txt +655 -0
- package/mcp-erpnext.mjs +1578 -75
- package/package.json +3 -1
- package/ui-dist/buy-evidence-viewer/index.html +2562 -0
- package/ui-dist/chart-viewer/index.html +28 -28
package/mcp-erpnext.mjs
CHANGED
|
@@ -1784,14 +1784,14 @@ var require_code3 = __commonJS({
|
|
|
1784
1784
|
}
|
|
1785
1785
|
exports.callValidateCode = callValidateCode;
|
|
1786
1786
|
var newRegExp = (0, codegen_1._)`new RegExp`;
|
|
1787
|
-
function usePattern({ gen, it: { opts } },
|
|
1787
|
+
function usePattern({ gen, it: { opts } }, pattern2) {
|
|
1788
1788
|
const u = opts.unicodeRegExp ? "u" : "";
|
|
1789
1789
|
const { regExp } = opts.code;
|
|
1790
|
-
const rx = regExp(
|
|
1790
|
+
const rx = regExp(pattern2, u);
|
|
1791
1791
|
return gen.scopeValue("pattern", {
|
|
1792
1792
|
key: rx.toString(),
|
|
1793
1793
|
ref: rx,
|
|
1794
|
-
code: (0, codegen_1._)`${regExp.code === "new RegExp" ? newRegExp : (0, util_2.useFunc)(gen, regExp)}(${
|
|
1794
|
+
code: (0, codegen_1._)`${regExp.code === "new RegExp" ? newRegExp : (0, util_2.useFunc)(gen, regExp)}(${pattern2}, ${u})`
|
|
1795
1795
|
});
|
|
1796
1796
|
}
|
|
1797
1797
|
exports.usePattern = usePattern;
|
|
@@ -6901,7 +6901,7 @@ var require_discriminator2 = __commonJS({
|
|
|
6901
6901
|
error: error2,
|
|
6902
6902
|
code(cxt) {
|
|
6903
6903
|
const { gen, data, schema, parentSchema, it } = cxt;
|
|
6904
|
-
const { oneOf } = parentSchema;
|
|
6904
|
+
const { oneOf: oneOf2 } = parentSchema;
|
|
6905
6905
|
if (!it.opts.discriminator) {
|
|
6906
6906
|
throw new Error("discriminator: requires discriminator option");
|
|
6907
6907
|
}
|
|
@@ -6910,7 +6910,7 @@ var require_discriminator2 = __commonJS({
|
|
|
6910
6910
|
throw new Error("discriminator: requires propertyName");
|
|
6911
6911
|
if (schema.mapping)
|
|
6912
6912
|
throw new Error("discriminator: mapping is not supported");
|
|
6913
|
-
if (!
|
|
6913
|
+
if (!oneOf2)
|
|
6914
6914
|
throw new Error("discriminator: requires oneOf keyword");
|
|
6915
6915
|
const valid = gen.let("valid", false);
|
|
6916
6916
|
const tag2 = gen.const("tag", (0, codegen_1._)`${data}${(0, codegen_1.getProperty)(tagName)}`);
|
|
@@ -6938,8 +6938,8 @@ var require_discriminator2 = __commonJS({
|
|
|
6938
6938
|
const oneOfMapping = {};
|
|
6939
6939
|
const topRequired = hasRequired(parentSchema);
|
|
6940
6940
|
let tagRequired = true;
|
|
6941
|
-
for (let i = 0; i <
|
|
6942
|
-
let sch =
|
|
6941
|
+
for (let i = 0; i < oneOf2.length; i++) {
|
|
6942
|
+
let sch = oneOf2[i];
|
|
6943
6943
|
if ((sch === null || sch === void 0 ? void 0 : sch.$ref) && !(0, util_1.schemaHasRulesButRef)(sch, it.self.RULES)) {
|
|
6944
6944
|
const ref = sch.$ref;
|
|
6945
6945
|
sch = compile_1.resolveRef.call(it.self, it.schemaEnv.root, it.baseId, ref);
|
|
@@ -9021,9 +9021,9 @@ ${indent}`) + "'";
|
|
|
9021
9021
|
return quotedString(value, ctx);
|
|
9022
9022
|
}
|
|
9023
9023
|
const indent = ctx.indent || (ctx.forceBlockIndent || containsDocumentMarker(value) ? " " : "");
|
|
9024
|
-
const
|
|
9024
|
+
const literal3 = blockQuote === "literal" ? true : blockQuote === "folded" || type === Scalar.Scalar.BLOCK_FOLDED ? false : type === Scalar.Scalar.BLOCK_LITERAL ? true : !lineLengthOverLimit(value, lineWidth, indent.length);
|
|
9025
9025
|
if (!value)
|
|
9026
|
-
return
|
|
9026
|
+
return literal3 ? "|\n" : ">\n";
|
|
9027
9027
|
let chomp;
|
|
9028
9028
|
let endStart;
|
|
9029
9029
|
for (endStart = value.length; endStart > 0; --endStart) {
|
|
@@ -9072,7 +9072,7 @@ ${indent}`) + "'";
|
|
|
9072
9072
|
if (onComment)
|
|
9073
9073
|
onComment();
|
|
9074
9074
|
}
|
|
9075
|
-
if (!
|
|
9075
|
+
if (!literal3) {
|
|
9076
9076
|
const foldedValue = value.replace(/\n+/g, "\n$&").replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, "$1$2").replace(/\n+/g, `$&${indent}`);
|
|
9077
9077
|
let literalFallback = false;
|
|
9078
9078
|
const foldOptions = getFoldOptions(ctx, true);
|
|
@@ -16815,8 +16815,8 @@ var $ZodCheckUpperCase = /* @__PURE__ */ $constructor("$ZodCheckUpperCase", (ins
|
|
|
16815
16815
|
var $ZodCheckIncludes = /* @__PURE__ */ $constructor("$ZodCheckIncludes", (inst, def) => {
|
|
16816
16816
|
$ZodCheck.init(inst, def);
|
|
16817
16817
|
const escapedRegex = escapeRegex(def.includes);
|
|
16818
|
-
const
|
|
16819
|
-
def.pattern =
|
|
16818
|
+
const pattern2 = new RegExp(typeof def.position === "number" ? `^.{${def.position},}${escapedRegex}` : escapedRegex);
|
|
16819
|
+
def.pattern = pattern2;
|
|
16820
16820
|
inst._zod.check = (payload) => {
|
|
16821
16821
|
if (payload.value.includes(def.includes, def.position))
|
|
16822
16822
|
return;
|
|
@@ -16833,8 +16833,8 @@ var $ZodCheckIncludes = /* @__PURE__ */ $constructor("$ZodCheckIncludes", (inst,
|
|
|
16833
16833
|
});
|
|
16834
16834
|
var $ZodCheckStartsWith = /* @__PURE__ */ $constructor("$ZodCheckStartsWith", (inst, def) => {
|
|
16835
16835
|
$ZodCheck.init(inst, def);
|
|
16836
|
-
const
|
|
16837
|
-
def.pattern ?? (def.pattern =
|
|
16836
|
+
const pattern2 = new RegExp(`^${escapeRegex(def.prefix)}.*`);
|
|
16837
|
+
def.pattern ?? (def.pattern = pattern2);
|
|
16838
16838
|
inst._zod.check = (payload) => {
|
|
16839
16839
|
if (payload.value.startsWith(def.prefix))
|
|
16840
16840
|
return;
|
|
@@ -16851,8 +16851,8 @@ var $ZodCheckStartsWith = /* @__PURE__ */ $constructor("$ZodCheckStartsWith", (i
|
|
|
16851
16851
|
});
|
|
16852
16852
|
var $ZodCheckEndsWith = /* @__PURE__ */ $constructor("$ZodCheckEndsWith", (inst, def) => {
|
|
16853
16853
|
$ZodCheck.init(inst, def);
|
|
16854
|
-
const
|
|
16855
|
-
def.pattern ?? (def.pattern =
|
|
16854
|
+
const pattern2 = new RegExp(`.*${escapeRegex(def.suffix)}$`);
|
|
16855
|
+
def.pattern ?? (def.pattern = pattern2);
|
|
16856
16856
|
inst._zod.check = (payload) => {
|
|
16857
16857
|
if (payload.value.endsWith(def.suffix))
|
|
16858
16858
|
return;
|
|
@@ -18369,8 +18369,8 @@ var $ZodOptional = /* @__PURE__ */ $constructor("$ZodOptional", (inst, def) => {
|
|
|
18369
18369
|
return values ? /* @__PURE__ */ new Set([...values, void 0]) : void 0;
|
|
18370
18370
|
});
|
|
18371
18371
|
defineLazyInternal(inst, "pattern", (zod) => {
|
|
18372
|
-
const
|
|
18373
|
-
return
|
|
18372
|
+
const pattern2 = zod.def.innerType._zod.pattern;
|
|
18373
|
+
return pattern2 ? new RegExp(`^(${cleanRegex(pattern2.source)})?$`) : void 0;
|
|
18374
18374
|
});
|
|
18375
18375
|
inst._zod.parse = (payload, ctx) => {
|
|
18376
18376
|
if (payload.value === void 0) {
|
|
@@ -18397,8 +18397,8 @@ var $ZodNullable = /* @__PURE__ */ $constructor("$ZodNullable", (inst, def) => {
|
|
|
18397
18397
|
defineLazyInternal(inst, "optin", (zod) => zod.def.innerType._zod.optin);
|
|
18398
18398
|
defineLazyInternal(inst, "optout", (zod) => zod.def.innerType._zod.optout);
|
|
18399
18399
|
defineLazyInternal(inst, "pattern", (zod) => {
|
|
18400
|
-
const
|
|
18401
|
-
return
|
|
18400
|
+
const pattern2 = zod.def.innerType._zod.pattern;
|
|
18401
|
+
return pattern2 ? new RegExp(`^(${cleanRegex(pattern2.source)}|null)$`) : void 0;
|
|
18402
18402
|
});
|
|
18403
18403
|
defineLazyInternal(inst, "values", (zod) => {
|
|
18404
18404
|
return zod.def.innerType._zod.values ? /* @__PURE__ */ new Set([...zod.def.innerType._zod.values, null]) : void 0;
|
|
@@ -19492,12 +19492,12 @@ function _length(length, params) {
|
|
|
19492
19492
|
});
|
|
19493
19493
|
}
|
|
19494
19494
|
// @__NO_SIDE_EFFECTS__
|
|
19495
|
-
function _regex(
|
|
19495
|
+
function _regex(pattern2, params) {
|
|
19496
19496
|
return new $ZodCheckRegex({
|
|
19497
19497
|
check: "string_format",
|
|
19498
19498
|
format: "regex",
|
|
19499
19499
|
...normalizeParams(params),
|
|
19500
|
-
pattern
|
|
19500
|
+
pattern: pattern2
|
|
19501
19501
|
});
|
|
19502
19502
|
}
|
|
19503
19503
|
// @__NO_SIDE_EFFECTS__
|
|
@@ -20183,9 +20183,9 @@ var addDivisor = (agg, value) => {
|
|
|
20183
20183
|
if (!agg.multipleOf.includes(value))
|
|
20184
20184
|
agg.multipleOf.push(value);
|
|
20185
20185
|
};
|
|
20186
|
-
var addPattern = (agg,
|
|
20186
|
+
var addPattern = (agg, pattern2) => {
|
|
20187
20187
|
agg.patterns ?? (agg.patterns = /* @__PURE__ */ new Set());
|
|
20188
|
-
agg.patterns.add(
|
|
20188
|
+
agg.patterns.add(pattern2);
|
|
20189
20189
|
};
|
|
20190
20190
|
var intersectMime = (agg, mime) => {
|
|
20191
20191
|
agg.mime = agg.mime ? agg.mime.filter((m) => mime.includes(m)) : [...mime];
|
|
@@ -20250,8 +20250,8 @@ function aggregateChecks(schema) {
|
|
|
20250
20250
|
}
|
|
20251
20251
|
if (bag.mime)
|
|
20252
20252
|
intersectMime(agg, bag.mime);
|
|
20253
|
-
for (const
|
|
20254
|
-
addPattern(agg,
|
|
20253
|
+
for (const pattern2 of bag.patterns ?? [])
|
|
20254
|
+
addPattern(agg, pattern2);
|
|
20255
20255
|
return agg;
|
|
20256
20256
|
}
|
|
20257
20257
|
var formatMap = {
|
|
@@ -20433,11 +20433,11 @@ var nanProcessor = (schema, ctx, json, params) => {
|
|
|
20433
20433
|
};
|
|
20434
20434
|
var templateLiteralProcessor = (schema, _ctx, json, _params) => {
|
|
20435
20435
|
const _json = json;
|
|
20436
|
-
const
|
|
20437
|
-
if (!
|
|
20436
|
+
const pattern2 = schema._zod.pattern;
|
|
20437
|
+
if (!pattern2)
|
|
20438
20438
|
throw new Error("Pattern not found in template literal");
|
|
20439
20439
|
_json.type = "string";
|
|
20440
|
-
_json.pattern =
|
|
20440
|
+
_json.pattern = pattern2.source;
|
|
20441
20441
|
};
|
|
20442
20442
|
var fileProcessor = (schema, _ctx, json, _params) => {
|
|
20443
20443
|
const _json = json;
|
|
@@ -20704,8 +20704,8 @@ function rewriteKeyNames(ctx) {
|
|
|
20704
20704
|
bySchema.set(entry.schema, entry);
|
|
20705
20705
|
}
|
|
20706
20706
|
const rewrites = /* @__PURE__ */ new Map();
|
|
20707
|
-
for (const
|
|
20708
|
-
const seen = ctx.seen.get(
|
|
20707
|
+
for (const record3 of pendingRecords.get(ctx) ?? []) {
|
|
20708
|
+
const seen = ctx.seen.get(record3);
|
|
20709
20709
|
const names = (seen?.def ?? seen?.schema)?.propertyNames;
|
|
20710
20710
|
if (!names || names === true || rewrites.has(names))
|
|
20711
20711
|
continue;
|
|
@@ -20735,8 +20735,8 @@ var recordProcessor = (schema, ctx, _json, params) => {
|
|
|
20735
20735
|
path: [...params.path, "patternProperties", "*"]
|
|
20736
20736
|
});
|
|
20737
20737
|
json.patternProperties = {};
|
|
20738
|
-
for (const
|
|
20739
|
-
assignProp(json.patternProperties, exactPattern(
|
|
20738
|
+
for (const pattern2 of patterns) {
|
|
20739
|
+
assignProp(json.patternProperties, exactPattern(pattern2).source, valueSchema);
|
|
20740
20740
|
}
|
|
20741
20741
|
} else {
|
|
20742
20742
|
if (ctx.target === "draft-07" || ctx.target === "draft-2020-12") {
|
|
@@ -26265,8 +26265,8 @@ function zodEmittedPattern(schema) {
|
|
|
26265
26265
|
return typeof jsonSchema.pattern === "string" ? jsonSchema.pattern : void 0;
|
|
26266
26266
|
}
|
|
26267
26267
|
var DATETIME_FRACTION_DIGITS = /\\\.\\d\{(\d+)\}/;
|
|
26268
|
-
function datetimeReferenceSchemas(
|
|
26269
|
-
const fractionDigits = DATETIME_FRACTION_DIGITS.exec(
|
|
26268
|
+
function datetimeReferenceSchemas(pattern2) {
|
|
26269
|
+
const fractionDigits = DATETIME_FRACTION_DIGITS.exec(pattern2);
|
|
26270
26270
|
const precisions = [
|
|
26271
26271
|
void 0,
|
|
26272
26272
|
-1,
|
|
@@ -26279,7 +26279,7 @@ function datetimeReferenceSchemas(pattern) {
|
|
|
26279
26279
|
precision
|
|
26280
26280
|
}))));
|
|
26281
26281
|
}
|
|
26282
|
-
function referencePatternsForFormat(format,
|
|
26282
|
+
function referencePatternsForFormat(format, pattern2) {
|
|
26283
26283
|
let referenceSchemas;
|
|
26284
26284
|
switch (format) {
|
|
26285
26285
|
case "email":
|
|
@@ -26292,14 +26292,14 @@ function referencePatternsForFormat(format, pattern) {
|
|
|
26292
26292
|
referenceSchemas = [iso_exports.date()];
|
|
26293
26293
|
break;
|
|
26294
26294
|
case "date-time":
|
|
26295
|
-
referenceSchemas = datetimeReferenceSchemas(
|
|
26295
|
+
referenceSchemas = datetimeReferenceSchemas(pattern2);
|
|
26296
26296
|
break;
|
|
26297
26297
|
}
|
|
26298
26298
|
return new Set(referenceSchemas.map((schema) => zodEmittedPattern(schema)).filter((emitted) => emitted !== void 0));
|
|
26299
26299
|
}
|
|
26300
|
-
function isLibraryFormatPattern(format,
|
|
26300
|
+
function isLibraryFormatPattern(format, pattern2, vendor) {
|
|
26301
26301
|
if (vendor !== "zod") return true;
|
|
26302
|
-
return referencePatternsForFormat(format,
|
|
26302
|
+
return referencePatternsForFormat(format, pattern2).has(pattern2);
|
|
26303
26303
|
}
|
|
26304
26304
|
function promptArgumentsFromStandardSchema(schema) {
|
|
26305
26305
|
const jsonSchema = standardSchemaToJsonSchema(schema, "input");
|
|
@@ -29237,14 +29237,14 @@ var require_code = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
29237
29237
|
}
|
|
29238
29238
|
exports.callValidateCode = callValidateCode;
|
|
29239
29239
|
const newRegExp = (0, codegen_1._)`new RegExp`;
|
|
29240
|
-
function usePattern({ gen, it: { opts } },
|
|
29240
|
+
function usePattern({ gen, it: { opts } }, pattern2) {
|
|
29241
29241
|
const u = opts.unicodeRegExp ? "u" : "";
|
|
29242
29242
|
const { regExp } = opts.code;
|
|
29243
|
-
const rx = regExp(
|
|
29243
|
+
const rx = regExp(pattern2, u);
|
|
29244
29244
|
return gen.scopeValue("pattern", {
|
|
29245
29245
|
key: rx.toString(),
|
|
29246
29246
|
ref: rx,
|
|
29247
|
-
code: (0, codegen_1._)`${regExp.code === "new RegExp" ? newRegExp : (0, util_2.useFunc)(gen, regExp)}(${
|
|
29247
|
+
code: (0, codegen_1._)`${regExp.code === "new RegExp" ? newRegExp : (0, util_2.useFunc)(gen, regExp)}(${pattern2}, ${u})`
|
|
29248
29248
|
});
|
|
29249
29249
|
}
|
|
29250
29250
|
exports.usePattern = usePattern;
|
|
@@ -32907,12 +32907,12 @@ var require_discriminator = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
32907
32907
|
},
|
|
32908
32908
|
code(cxt) {
|
|
32909
32909
|
const { gen, data, schema, parentSchema, it } = cxt;
|
|
32910
|
-
const { oneOf } = parentSchema;
|
|
32910
|
+
const { oneOf: oneOf2 } = parentSchema;
|
|
32911
32911
|
if (!it.opts.discriminator) throw new Error("discriminator: requires discriminator option");
|
|
32912
32912
|
const tagName = schema.propertyName;
|
|
32913
32913
|
if (typeof tagName != "string") throw new Error("discriminator: requires propertyName");
|
|
32914
32914
|
if (schema.mapping) throw new Error("discriminator: mapping is not supported");
|
|
32915
|
-
if (!
|
|
32915
|
+
if (!oneOf2) throw new Error("discriminator: requires oneOf keyword");
|
|
32916
32916
|
const valid = gen.let("valid", false);
|
|
32917
32917
|
const tag2 = gen.const("tag", (0, codegen_1._)`${data}${(0, codegen_1.getProperty)(tagName)}`);
|
|
32918
32918
|
gen.if((0, codegen_1._)`typeof ${tag2} == "string"`, () => validateMapping(), () => cxt.error(false, {
|
|
@@ -32950,8 +32950,8 @@ var require_discriminator = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
32950
32950
|
const oneOfMapping = {};
|
|
32951
32951
|
const topRequired = hasRequired(parentSchema);
|
|
32952
32952
|
let tagRequired = true;
|
|
32953
|
-
for (let i = 0; i <
|
|
32954
|
-
let sch =
|
|
32953
|
+
for (let i = 0; i < oneOf2.length; i++) {
|
|
32954
|
+
let sch = oneOf2[i];
|
|
32955
32955
|
if ((sch === null || sch === void 0 ? void 0 : sch.$ref) && !(0, util_1.schemaHasRulesButRef)(sch, it.self.RULES)) {
|
|
32956
32956
|
const ref = sch.$ref;
|
|
32957
32957
|
sch = compile_1.resolveRef.call(it.self, it.schemaEnv.root, it.baseId, ref);
|
|
@@ -39730,12 +39730,12 @@ var Node = class _Node {
|
|
|
39730
39730
|
let node2 = this;
|
|
39731
39731
|
for (let i = 0, len = tokens.length; i < len; i++) {
|
|
39732
39732
|
const token = tokens[i];
|
|
39733
|
-
const
|
|
39733
|
+
const pattern2 = token.length === 1 ? token === "*" ? i === len - 1 ? ["", "", ONLY_WILDCARD_REG_EXP_STR] : ["", "", LABEL_REG_EXP_STR] : null : token === "/*" ? ["", "", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
|
|
39734
39734
|
let nextNode;
|
|
39735
|
-
if (
|
|
39736
|
-
const name =
|
|
39737
|
-
let regexpStr =
|
|
39738
|
-
if (name &&
|
|
39735
|
+
if (pattern2) {
|
|
39736
|
+
const name = pattern2[1];
|
|
39737
|
+
let regexpStr = pattern2[2] || LABEL_REG_EXP_STR;
|
|
39738
|
+
if (name && pattern2[2]) {
|
|
39739
39739
|
if (regexpStr === ".*") {
|
|
39740
39740
|
throw PATH_ERROR;
|
|
39741
39741
|
}
|
|
@@ -40059,17 +40059,17 @@ var Node2 = class _Node2 {
|
|
|
40059
40059
|
let i = 0;
|
|
40060
40060
|
for (const p of parts) {
|
|
40061
40061
|
const nextP = parts[++i];
|
|
40062
|
-
const
|
|
40063
|
-
const isParam = Array.isArray(
|
|
40064
|
-
const key = isParam ?
|
|
40062
|
+
const pattern2 = getPattern(p, nextP) || (nextP === void 0 && p && p.indexOf("*") === p.length - 1 ? p : null);
|
|
40063
|
+
const isParam = Array.isArray(pattern2);
|
|
40064
|
+
const key = isParam ? pattern2[0] : pattern2 || p;
|
|
40065
40065
|
const child = curNode.#children[key] ||= new _Node2();
|
|
40066
|
-
if (
|
|
40067
|
-
child.#pattern =
|
|
40066
|
+
if (pattern2 && !child.#pattern) {
|
|
40067
|
+
child.#pattern = pattern2;
|
|
40068
40068
|
curNode.#patterns.push(child);
|
|
40069
40069
|
}
|
|
40070
40070
|
curNode = child;
|
|
40071
40071
|
if (isParam) {
|
|
40072
|
-
possibleKeys.add(
|
|
40072
|
+
possibleKeys.add(pattern2[1]);
|
|
40073
40073
|
}
|
|
40074
40074
|
}
|
|
40075
40075
|
curNode.#methods.push({
|
|
@@ -40122,19 +40122,19 @@ var Node2 = class _Node2 {
|
|
|
40122
40122
|
}
|
|
40123
40123
|
}
|
|
40124
40124
|
for (const child of node2.#patterns) {
|
|
40125
|
-
const
|
|
40125
|
+
const pattern2 = child.#pattern;
|
|
40126
40126
|
const params = node2.#params === emptyParams ? {} : { ...node2.#params };
|
|
40127
|
-
if (typeof
|
|
40128
|
-
if (
|
|
40127
|
+
if (typeof pattern2 === "string") {
|
|
40128
|
+
if (pattern2 === "*" || part.startsWith(pattern2.slice(0, -1))) {
|
|
40129
40129
|
this.#pushHandlerSets(handlerSets, child, method, node2.#params);
|
|
40130
|
-
if (
|
|
40130
|
+
if (pattern2 === "*") {
|
|
40131
40131
|
child.#params = params;
|
|
40132
40132
|
tempNodes.push(child);
|
|
40133
40133
|
}
|
|
40134
40134
|
}
|
|
40135
40135
|
continue;
|
|
40136
40136
|
}
|
|
40137
|
-
const [, name, matcher] =
|
|
40137
|
+
const [, name, matcher] = pattern2;
|
|
40138
40138
|
if (!part && matcher === true) {
|
|
40139
40139
|
continue;
|
|
40140
40140
|
}
|
|
@@ -48158,6 +48158,7 @@ var CHART_META = viewer("chart-viewer");
|
|
|
48158
48158
|
var KANBAN_META = viewer("kanban-viewer");
|
|
48159
48159
|
var KPI_META = viewer("kpi-viewer");
|
|
48160
48160
|
var FUNNEL_META = viewer("funnel-viewer");
|
|
48161
|
+
var BUY_EVIDENCE_META = viewer("buy-evidence-viewer");
|
|
48161
48162
|
|
|
48162
48163
|
// src/runtime.ts
|
|
48163
48164
|
var isDeno2 = typeof globalThis.Deno?.version?.deno === "string";
|
|
@@ -48387,6 +48388,24 @@ function decodeBase64File(contentBase64, maxBytes) {
|
|
|
48387
48388
|
}
|
|
48388
48389
|
return Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
48389
48390
|
}
|
|
48391
|
+
function normalizeFrappeSiteUrl(baseUrl) {
|
|
48392
|
+
if (typeof baseUrl !== "string" || baseUrl.trim() === "") {
|
|
48393
|
+
throw new Error(
|
|
48394
|
+
"[FrappeClient] baseUrl is required to derive site identity"
|
|
48395
|
+
);
|
|
48396
|
+
}
|
|
48397
|
+
let url2;
|
|
48398
|
+
try {
|
|
48399
|
+
url2 = new URL(baseUrl);
|
|
48400
|
+
} catch {
|
|
48401
|
+
throw new Error("[FrappeClient] baseUrl must be an absolute URL");
|
|
48402
|
+
}
|
|
48403
|
+
if (url2.protocol !== "http:" && url2.protocol !== "https:") {
|
|
48404
|
+
throw new Error("[FrappeClient] baseUrl must be http or https");
|
|
48405
|
+
}
|
|
48406
|
+
const path = url2.pathname.replace(/\/+$/, "");
|
|
48407
|
+
return `${url2.protocol}//${url2.host}${path}`;
|
|
48408
|
+
}
|
|
48390
48409
|
var FrappeAPIError = class extends Error {
|
|
48391
48410
|
/**
|
|
48392
48411
|
* @param message - Human-readable error description
|
|
@@ -48473,6 +48492,17 @@ var FrappeClient = class {
|
|
|
48473
48492
|
this.retryMethods = config2.retryMethods ?? DEFAULT_RETRY_METHODS;
|
|
48474
48493
|
this.cache = config2.cache ?? new MemoryCache();
|
|
48475
48494
|
}
|
|
48495
|
+
/**
|
|
48496
|
+
* Normalized site URL of this configured client: origin + site path, with
|
|
48497
|
+
* credentials, query, and fragment stripped, and without a trailing slash.
|
|
48498
|
+
*
|
|
48499
|
+
* This is the preimage of the Buy capture `sourceInstance.siteId` SHA-256.
|
|
48500
|
+
* It is not an ERP database UUID and must not be copied onto the wire when
|
|
48501
|
+
* the opaque site identifier is sufficient.
|
|
48502
|
+
*/
|
|
48503
|
+
normalizedSiteUrl() {
|
|
48504
|
+
return normalizeFrappeSiteUrl(this.baseUrl);
|
|
48505
|
+
}
|
|
48476
48506
|
// ── Private HTTP helpers ────────────────────────────────────────────────────
|
|
48477
48507
|
buildHeaders(includeJsonContentType = true) {
|
|
48478
48508
|
const headers = {
|
|
@@ -49154,10 +49184,10 @@ function roundedTotalFallbackWarning(original, patched) {
|
|
|
49154
49184
|
|
|
49155
49185
|
// src/tools/sales.ts
|
|
49156
49186
|
function viewerDocument(value, doctype, name) {
|
|
49157
|
-
const
|
|
49187
|
+
const record3 = typeof value === "object" && value !== null ? value : {};
|
|
49158
49188
|
return {
|
|
49159
|
-
...
|
|
49160
|
-
...typeof
|
|
49189
|
+
...record3,
|
|
49190
|
+
...typeof record3.name === "string" || !name ? {} : { name },
|
|
49161
49191
|
doctype
|
|
49162
49192
|
};
|
|
49163
49193
|
}
|
|
@@ -51665,8 +51695,8 @@ async function applyAssignment(doctype, name, assignment, ctx, failureContext) {
|
|
|
51665
51695
|
throw new Error(`${failureContext}: ${reason}`, { cause: error2 });
|
|
51666
51696
|
}
|
|
51667
51697
|
const todos = Array.isArray(nativeResult) ? nativeResult.map((todo) => {
|
|
51668
|
-
const
|
|
51669
|
-
return { owner:
|
|
51698
|
+
const record3 = todo;
|
|
51699
|
+
return { owner: record3.owner, name: record3.name };
|
|
51670
51700
|
}) : [];
|
|
51671
51701
|
return { notify_user: true, assignees: assignment.assignees, todos };
|
|
51672
51702
|
}
|
|
@@ -51683,8 +51713,8 @@ async function removeAssignment(doctype, name, assignee, ctx, failureContext) {
|
|
|
51683
51713
|
throw new Error(`${failureContext}: ${reason}`, { cause: error2 });
|
|
51684
51714
|
}
|
|
51685
51715
|
const todos = Array.isArray(nativeResult) ? nativeResult.map((todo) => {
|
|
51686
|
-
const
|
|
51687
|
-
return { owner:
|
|
51716
|
+
const record3 = todo;
|
|
51717
|
+
return { owner: record3.owner, name: record3.name };
|
|
51688
51718
|
}) : [];
|
|
51689
51719
|
return { removed: assignee, remaining: todos };
|
|
51690
51720
|
}
|
|
@@ -54973,11 +55003,11 @@ var setupTools = [
|
|
|
54973
55003
|
filters.push(["enabled", "=", 1]);
|
|
54974
55004
|
}
|
|
54975
55005
|
if (input.search) {
|
|
54976
|
-
const
|
|
55006
|
+
const literal3 = input.search.replace(
|
|
54977
55007
|
/[\\%_]/g,
|
|
54978
55008
|
(match2) => `\\${match2}`
|
|
54979
55009
|
);
|
|
54980
|
-
filters.push(["full_name", "like", `%${
|
|
55010
|
+
filters.push(["full_name", "like", `%${literal3}%`]);
|
|
54981
55011
|
}
|
|
54982
55012
|
const docs = await ctx.client.list("User", {
|
|
54983
55013
|
fields: ["name", "full_name", "enabled"],
|
|
@@ -57437,6 +57467,1349 @@ var kanbanTools = [
|
|
|
57437
57467
|
}
|
|
57438
57468
|
];
|
|
57439
57469
|
|
|
57470
|
+
// src/buy/identities.ts
|
|
57471
|
+
var BUY_SOURCE_CAPTURE_SCHEMA = "io.casys.mcp-erpnext.buy-source-capture/1.0";
|
|
57472
|
+
var BUY_RECORDED_RESULT_SCHEMA = "io.casys.mcp-erpnext.buy-recorded-result/1.0";
|
|
57473
|
+
var BUY_RECORDED_SESSION_SCHEMA = "io.casys.mcp-erpnext.buy-recorded-session/1.0";
|
|
57474
|
+
var BUY_EVIDENCE_VIEWER_URI = "ui://mcp-erpnext/buy-evidence-viewer";
|
|
57475
|
+
var BUY_VIEW_APP_MANIFEST_URI = "ui://mcp-erpnext/buy-evidence-manifest";
|
|
57476
|
+
var BUY_VIEW_APP_ID = "io.casys.mcp-erpnext.buy-evidence";
|
|
57477
|
+
var BUY_VIEW_APP_TITLE = "ERPNext Buy Evidence";
|
|
57478
|
+
var BUY_VIEW_APP_VERSION = "3.1.0-beta.9";
|
|
57479
|
+
var BUY_SOURCE_INSTANCE_KIND = "erpnext-site";
|
|
57480
|
+
var BUY_CAPTURE_DOCTYPE = [
|
|
57481
|
+
"Item",
|
|
57482
|
+
"BOM",
|
|
57483
|
+
"Item Price",
|
|
57484
|
+
"Supplier Quotation",
|
|
57485
|
+
"Supplier",
|
|
57486
|
+
"Price List",
|
|
57487
|
+
"UOM",
|
|
57488
|
+
"Currency Exchange"
|
|
57489
|
+
];
|
|
57490
|
+
var BUY_CAPTURE_MAX_DOCUMENTS = 32;
|
|
57491
|
+
var BUY_CAPTURE_MAX_CHILD_ROWS = 200;
|
|
57492
|
+
var BUY_CAPTURE_MAX_CANONICAL_BYTES = 262144;
|
|
57493
|
+
var BUY_CAPTURE_MAX_NAME_LENGTH = 140;
|
|
57494
|
+
var BUY_DOCTYPE_SOURCE_CATEGORY = {
|
|
57495
|
+
Item: "item",
|
|
57496
|
+
BOM: "bom",
|
|
57497
|
+
"Item Price": "catalogue-price",
|
|
57498
|
+
"Supplier Quotation": "supplier-quotation",
|
|
57499
|
+
Supplier: "supplier",
|
|
57500
|
+
"Price List": "price-list",
|
|
57501
|
+
UOM: "uom",
|
|
57502
|
+
"Currency Exchange": "currency-exchange"
|
|
57503
|
+
};
|
|
57504
|
+
var BUY_CONSISTENCY_KIND = "repeated-read";
|
|
57505
|
+
var BUY_CONSISTENCY_READS = 2;
|
|
57506
|
+
|
|
57507
|
+
// src/buy/errors.ts
|
|
57508
|
+
var BuyCaptureError = class extends Error {
|
|
57509
|
+
code;
|
|
57510
|
+
retryable;
|
|
57511
|
+
context;
|
|
57512
|
+
recovery;
|
|
57513
|
+
constructor(code, message2, options) {
|
|
57514
|
+
super(message2);
|
|
57515
|
+
this.name = "BuyCaptureError";
|
|
57516
|
+
this.code = code;
|
|
57517
|
+
this.retryable = options.retryable === true;
|
|
57518
|
+
this.context = Object.freeze({ ...options.context ?? {} });
|
|
57519
|
+
this.recovery = options.recovery;
|
|
57520
|
+
}
|
|
57521
|
+
};
|
|
57522
|
+
|
|
57523
|
+
// src/buy/json.ts
|
|
57524
|
+
var FINGERPRINT = /^sha256:[a-f0-9]{64}$/;
|
|
57525
|
+
var ISO_MILLISECONDS = /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/;
|
|
57526
|
+
var CALENDAR_DATE = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/;
|
|
57527
|
+
var FRAPPE_DATETIME = /^[0-9]{4}-[0-9]{2}-[0-9]{2}[ T][0-9]{2}:[0-9]{2}:[0-9]{2}(?:\.[0-9]+)?$/;
|
|
57528
|
+
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set([
|
|
57529
|
+
"api_key",
|
|
57530
|
+
"api_secret",
|
|
57531
|
+
"authorization",
|
|
57532
|
+
"baseUrl",
|
|
57533
|
+
"command",
|
|
57534
|
+
"commands",
|
|
57535
|
+
"cookie",
|
|
57536
|
+
"credentials",
|
|
57537
|
+
"endpoint",
|
|
57538
|
+
"password",
|
|
57539
|
+
"source_path",
|
|
57540
|
+
"sourcePath",
|
|
57541
|
+
"stagedPath"
|
|
57542
|
+
]);
|
|
57543
|
+
function record2(value, name) {
|
|
57544
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
57545
|
+
throw new TypeError(`${name} must be an object.`);
|
|
57546
|
+
}
|
|
57547
|
+
return value;
|
|
57548
|
+
}
|
|
57549
|
+
function exactKeys(value, keys, name) {
|
|
57550
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
57551
|
+
if (ownKeys.some((key) => typeof key !== "string")) {
|
|
57552
|
+
throw new TypeError(`${name} contains missing or unsupported fields.`);
|
|
57553
|
+
}
|
|
57554
|
+
for (const key of ownKeys) {
|
|
57555
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
57556
|
+
if (!descriptor?.enumerable || !("value" in descriptor)) {
|
|
57557
|
+
throw new TypeError(`${name} contains missing or unsupported fields.`);
|
|
57558
|
+
}
|
|
57559
|
+
}
|
|
57560
|
+
const actual = ownKeys.toSorted();
|
|
57561
|
+
const expected = [...keys].toSorted();
|
|
57562
|
+
if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) {
|
|
57563
|
+
throw new TypeError(`${name} contains missing or unsupported fields.`);
|
|
57564
|
+
}
|
|
57565
|
+
}
|
|
57566
|
+
function exactRecord(value, keys, name) {
|
|
57567
|
+
const root = record2(value, name);
|
|
57568
|
+
exactKeys(root, keys, name);
|
|
57569
|
+
rejectForbiddenKeys(root, name);
|
|
57570
|
+
return root;
|
|
57571
|
+
}
|
|
57572
|
+
function denseArray(value, name) {
|
|
57573
|
+
if (!Array.isArray(value)) throw new TypeError(`${name} must be an array.`);
|
|
57574
|
+
const allowed = /* @__PURE__ */ new Set(["length"]);
|
|
57575
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
57576
|
+
allowed.add(String(index));
|
|
57577
|
+
}
|
|
57578
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
57579
|
+
if (ownKeys.length !== allowed.size || ownKeys.some((key) => typeof key !== "string" || !allowed.has(key))) {
|
|
57580
|
+
throw new TypeError(`${name} must be a dense, unadorned array.`);
|
|
57581
|
+
}
|
|
57582
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
57583
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
57584
|
+
if (!descriptor?.enumerable || !("value" in descriptor)) {
|
|
57585
|
+
throw new TypeError(`${name} must be a dense, unadorned array.`);
|
|
57586
|
+
}
|
|
57587
|
+
}
|
|
57588
|
+
return value;
|
|
57589
|
+
}
|
|
57590
|
+
function literal2(value, expected, name) {
|
|
57591
|
+
if (value !== expected) throw new TypeError(`${name} must be ${expected}.`);
|
|
57592
|
+
return expected;
|
|
57593
|
+
}
|
|
57594
|
+
function oneOf(value, allowed, name) {
|
|
57595
|
+
if (typeof value !== "string" || !allowed.includes(value)) {
|
|
57596
|
+
throw new TypeError(`${name} must be one of ${allowed.join(", ")}.`);
|
|
57597
|
+
}
|
|
57598
|
+
return value;
|
|
57599
|
+
}
|
|
57600
|
+
function nonEmpty(value, name) {
|
|
57601
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
57602
|
+
throw new TypeError(`${name} must be a non-empty string.`);
|
|
57603
|
+
}
|
|
57604
|
+
if (value.trim() !== value) {
|
|
57605
|
+
throw new TypeError(`${name} must not contain whitespace padding.`);
|
|
57606
|
+
}
|
|
57607
|
+
return value;
|
|
57608
|
+
}
|
|
57609
|
+
function pattern(value, expected, name) {
|
|
57610
|
+
if (typeof value !== "string" || !expected.test(value)) {
|
|
57611
|
+
throw new TypeError(`${name} has an invalid format.`);
|
|
57612
|
+
}
|
|
57613
|
+
return value;
|
|
57614
|
+
}
|
|
57615
|
+
function fingerprint(value, name) {
|
|
57616
|
+
return pattern(value, FINGERPRINT, name);
|
|
57617
|
+
}
|
|
57618
|
+
function canonicalTimestamp(value, name) {
|
|
57619
|
+
const timestamp = pattern(value, ISO_MILLISECONDS, name);
|
|
57620
|
+
try {
|
|
57621
|
+
if (new Date(timestamp).toISOString() !== timestamp) {
|
|
57622
|
+
throw new TypeError(`${name} is not canonical UTC.`);
|
|
57623
|
+
}
|
|
57624
|
+
} catch {
|
|
57625
|
+
throw new TypeError(`${name} is not canonical UTC.`);
|
|
57626
|
+
}
|
|
57627
|
+
return timestamp;
|
|
57628
|
+
}
|
|
57629
|
+
function calendarDate(value, name) {
|
|
57630
|
+
const date4 = pattern(value, CALENDAR_DATE, name);
|
|
57631
|
+
const parsed = /* @__PURE__ */ new Date(`${date4}T00:00:00.000Z`);
|
|
57632
|
+
if (Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== date4) {
|
|
57633
|
+
throw new TypeError(`${name} is not a real calendar date.`);
|
|
57634
|
+
}
|
|
57635
|
+
return date4;
|
|
57636
|
+
}
|
|
57637
|
+
function frappeDatetime(value, name) {
|
|
57638
|
+
const datetime3 = pattern(value, FRAPPE_DATETIME, name);
|
|
57639
|
+
calendarDate(datetime3.slice(0, 10), name);
|
|
57640
|
+
const [hour, minute, second] = datetime3.slice(11, 19).split(":").map(Number);
|
|
57641
|
+
if (hour > 23 || minute > 59 || second > 59) {
|
|
57642
|
+
throw new TypeError(`${name} is not a real Frappe datetime.`);
|
|
57643
|
+
}
|
|
57644
|
+
return datetime3;
|
|
57645
|
+
}
|
|
57646
|
+
function boundedArray(value, max, name) {
|
|
57647
|
+
const items = denseArray(value, name);
|
|
57648
|
+
if (items.length > max) {
|
|
57649
|
+
throw new TypeError(`${name} exceeds the ${max}-entry bound.`);
|
|
57650
|
+
}
|
|
57651
|
+
return items;
|
|
57652
|
+
}
|
|
57653
|
+
function rejectForbiddenKeys(value, name) {
|
|
57654
|
+
if (Array.isArray(value)) {
|
|
57655
|
+
const items = denseArray(value, name);
|
|
57656
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
57657
|
+
rejectForbiddenKeys(items[index], `${name}[${index}]`);
|
|
57658
|
+
}
|
|
57659
|
+
return;
|
|
57660
|
+
}
|
|
57661
|
+
if (typeof value !== "object" || value === null) return;
|
|
57662
|
+
const root = record2(value, name);
|
|
57663
|
+
for (const key of Object.keys(root)) {
|
|
57664
|
+
if (FORBIDDEN_KEYS.has(key)) {
|
|
57665
|
+
throw new TypeError(
|
|
57666
|
+
`${name}.${key} is a private credential, path, or command field and must not appear in the Buy contract.`
|
|
57667
|
+
);
|
|
57668
|
+
}
|
|
57669
|
+
rejectForbiddenKeys(root[key], `${name}.${key}`);
|
|
57670
|
+
}
|
|
57671
|
+
}
|
|
57672
|
+
function canonicalJson2(value, name) {
|
|
57673
|
+
if (value === null || typeof value === "boolean" || typeof value === "string") {
|
|
57674
|
+
return JSON.stringify(value);
|
|
57675
|
+
}
|
|
57676
|
+
if (typeof value === "number") {
|
|
57677
|
+
if (!Number.isFinite(value)) {
|
|
57678
|
+
throw new TypeError(`${name} must contain only finite JSON numbers.`);
|
|
57679
|
+
}
|
|
57680
|
+
return JSON.stringify(value);
|
|
57681
|
+
}
|
|
57682
|
+
if (Array.isArray(value)) {
|
|
57683
|
+
const items = denseArray(value, name);
|
|
57684
|
+
return `[${items.map((item, index) => canonicalJson2(item, `${name}[${index}]`)).join(",")}]`;
|
|
57685
|
+
}
|
|
57686
|
+
const root = record2(value, name);
|
|
57687
|
+
const prototype = Object.getPrototypeOf(root);
|
|
57688
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
57689
|
+
throw new TypeError(`${name} must contain only plain JSON objects.`);
|
|
57690
|
+
}
|
|
57691
|
+
const ownKeys = Reflect.ownKeys(root);
|
|
57692
|
+
if (ownKeys.some((key) => typeof key !== "string")) {
|
|
57693
|
+
throw new TypeError(`${name} must contain only string-keyed JSON objects.`);
|
|
57694
|
+
}
|
|
57695
|
+
const keys = ownKeys.toSorted();
|
|
57696
|
+
const fields = keys.map((key) => {
|
|
57697
|
+
const descriptor = Object.getOwnPropertyDescriptor(root, key);
|
|
57698
|
+
if (!descriptor?.enumerable || !("value" in descriptor)) {
|
|
57699
|
+
throw new TypeError(`${name}.${key} must be an enumerable JSON value.`);
|
|
57700
|
+
}
|
|
57701
|
+
if (descriptor.value === void 0) {
|
|
57702
|
+
throw new TypeError(`${name}.${key} must not be undefined.`);
|
|
57703
|
+
}
|
|
57704
|
+
return `${JSON.stringify(key)}:${canonicalJson2(descriptor.value, `${name}.${key}`)}`;
|
|
57705
|
+
});
|
|
57706
|
+
return `{${fields.join(",")}}`;
|
|
57707
|
+
}
|
|
57708
|
+
function utf8ByteCount(text) {
|
|
57709
|
+
return new TextEncoder().encode(text).byteLength;
|
|
57710
|
+
}
|
|
57711
|
+
async function sha256HexOfUtf8(text) {
|
|
57712
|
+
const bytes = new TextEncoder().encode(text);
|
|
57713
|
+
const digestBytes = new Uint8Array(
|
|
57714
|
+
await crypto.subtle.digest("SHA-256", bytes)
|
|
57715
|
+
);
|
|
57716
|
+
return Array.from(digestBytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
57717
|
+
}
|
|
57718
|
+
async function sha256FingerprintOfUtf8(text) {
|
|
57719
|
+
return `sha256:${await sha256HexOfUtf8(text)}`;
|
|
57720
|
+
}
|
|
57721
|
+
|
|
57722
|
+
// src/buy/projection.ts
|
|
57723
|
+
var DECIMAL = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;
|
|
57724
|
+
var CURRENCY = /^[A-Z]{3}$/;
|
|
57725
|
+
var SENSITIVE_FIELDS = /* @__PURE__ */ new Set([
|
|
57726
|
+
"api_key",
|
|
57727
|
+
"api_secret",
|
|
57728
|
+
"password",
|
|
57729
|
+
"email_id",
|
|
57730
|
+
"email",
|
|
57731
|
+
"mobile_no",
|
|
57732
|
+
"phone",
|
|
57733
|
+
"cell_number"
|
|
57734
|
+
]);
|
|
57735
|
+
var ITEM_FIELDS = [
|
|
57736
|
+
{ key: "item_code", kind: "string", required: true },
|
|
57737
|
+
{ key: "item_name", kind: "string" },
|
|
57738
|
+
{ key: "item_group", kind: "string" },
|
|
57739
|
+
{ key: "stock_uom", kind: "string" },
|
|
57740
|
+
{ key: "is_purchase_item", kind: "boolean" },
|
|
57741
|
+
{ key: "is_stock_item", kind: "boolean" },
|
|
57742
|
+
{ key: "disabled", kind: "boolean" }
|
|
57743
|
+
];
|
|
57744
|
+
var BOM_FIELDS = [
|
|
57745
|
+
{ key: "item", kind: "string", required: true },
|
|
57746
|
+
{ key: "item_name", kind: "string" },
|
|
57747
|
+
{ key: "quantity", kind: "decimal", required: true },
|
|
57748
|
+
{ key: "uom", kind: "string", required: true },
|
|
57749
|
+
{ key: "is_active", kind: "boolean" },
|
|
57750
|
+
{ key: "is_default", kind: "boolean" },
|
|
57751
|
+
{ key: "company", kind: "string" },
|
|
57752
|
+
{ key: "currency", kind: "currency" },
|
|
57753
|
+
{ key: "with_operations", kind: "boolean" }
|
|
57754
|
+
];
|
|
57755
|
+
var BOM_ITEM_FIELDS = [
|
|
57756
|
+
{ key: "item_code", kind: "string", required: true },
|
|
57757
|
+
{ key: "item_name", kind: "string" },
|
|
57758
|
+
{ key: "qty", kind: "decimal", required: true },
|
|
57759
|
+
{ key: "uom", kind: "string", required: true },
|
|
57760
|
+
{ key: "rate", kind: "decimal" },
|
|
57761
|
+
{ key: "amount", kind: "decimal" },
|
|
57762
|
+
{ key: "sourced_by_supplier", kind: "boolean" }
|
|
57763
|
+
];
|
|
57764
|
+
var BOM_OPERATION_FIELDS = [
|
|
57765
|
+
{ key: "operation", kind: "string", required: true },
|
|
57766
|
+
{ key: "time_in_mins", kind: "decimal" },
|
|
57767
|
+
{ key: "hour_rate", kind: "decimal" }
|
|
57768
|
+
];
|
|
57769
|
+
var ITEM_PRICE_FIELDS = [
|
|
57770
|
+
{ key: "item_code", kind: "string", required: true },
|
|
57771
|
+
{ key: "item_name", kind: "string" },
|
|
57772
|
+
{ key: "price_list", kind: "string" },
|
|
57773
|
+
{ key: "price_list_rate", kind: "decimal", required: true },
|
|
57774
|
+
{ key: "currency", kind: "currency", required: true },
|
|
57775
|
+
{ key: "uom", kind: "string" },
|
|
57776
|
+
{ key: "min_qty", kind: "decimal" },
|
|
57777
|
+
{ key: "valid_from", kind: "date" },
|
|
57778
|
+
{ key: "valid_upto", kind: "date" },
|
|
57779
|
+
{ key: "supplier", kind: "string" },
|
|
57780
|
+
{ key: "buying", kind: "boolean" },
|
|
57781
|
+
{ key: "selling", kind: "boolean" }
|
|
57782
|
+
];
|
|
57783
|
+
var SUPPLIER_QUOTATION_FIELDS = [
|
|
57784
|
+
{ key: "supplier", kind: "string", required: true },
|
|
57785
|
+
{ key: "supplier_name", kind: "string" },
|
|
57786
|
+
{ key: "transaction_date", kind: "date", required: true },
|
|
57787
|
+
{ key: "valid_till", kind: "date" },
|
|
57788
|
+
{ key: "currency", kind: "currency", required: true },
|
|
57789
|
+
{ key: "conversion_rate", kind: "decimal" },
|
|
57790
|
+
{ key: "status", kind: "string" },
|
|
57791
|
+
{ key: "company", kind: "string" },
|
|
57792
|
+
{ key: "price_list", kind: "string" }
|
|
57793
|
+
];
|
|
57794
|
+
var SUPPLIER_QUOTATION_ITEM_FIELDS = [
|
|
57795
|
+
{ key: "item_code", kind: "string", required: true },
|
|
57796
|
+
{ key: "item_name", kind: "string" },
|
|
57797
|
+
{ key: "qty", kind: "decimal", required: true },
|
|
57798
|
+
{ key: "uom", kind: "string", required: true },
|
|
57799
|
+
{ key: "rate", kind: "decimal" },
|
|
57800
|
+
{ key: "amount", kind: "decimal" },
|
|
57801
|
+
{ key: "price_list_rate", kind: "decimal" },
|
|
57802
|
+
{ key: "discount_percentage", kind: "decimal" },
|
|
57803
|
+
{ key: "supplier_part_no", kind: "string" }
|
|
57804
|
+
];
|
|
57805
|
+
var SUPPLIER_FIELDS = [
|
|
57806
|
+
{ key: "supplier_name", kind: "string", required: true },
|
|
57807
|
+
{ key: "supplier_group", kind: "string" },
|
|
57808
|
+
{ key: "supplier_type", kind: "string" },
|
|
57809
|
+
{ key: "country", kind: "string" },
|
|
57810
|
+
{ key: "default_currency", kind: "currency" },
|
|
57811
|
+
{ key: "disabled", kind: "boolean" }
|
|
57812
|
+
];
|
|
57813
|
+
var PRICE_LIST_FIELDS = [
|
|
57814
|
+
{ key: "currency", kind: "currency", required: true },
|
|
57815
|
+
{ key: "enabled", kind: "boolean" },
|
|
57816
|
+
{ key: "buying", kind: "boolean" },
|
|
57817
|
+
{ key: "selling", kind: "boolean" },
|
|
57818
|
+
{ key: "price_not_uom_dependent", kind: "boolean" }
|
|
57819
|
+
];
|
|
57820
|
+
var UOM_FIELDS = [
|
|
57821
|
+
{ key: "uom_name", kind: "string" },
|
|
57822
|
+
{ key: "must_be_whole_number", kind: "boolean" }
|
|
57823
|
+
];
|
|
57824
|
+
var CURRENCY_EXCHANGE_FIELDS = [
|
|
57825
|
+
{ key: "from_currency", kind: "currency", required: true },
|
|
57826
|
+
{ key: "to_currency", kind: "currency", required: true },
|
|
57827
|
+
{ key: "exchange_rate", kind: "decimal", required: true },
|
|
57828
|
+
{ key: "date", kind: "date", required: true }
|
|
57829
|
+
];
|
|
57830
|
+
var BUY_DOCTYPE_CHILD_TABLES = {
|
|
57831
|
+
BOM: [
|
|
57832
|
+
{ table: "items", fields: BOM_ITEM_FIELDS },
|
|
57833
|
+
{ table: "operations", fields: BOM_OPERATION_FIELDS }
|
|
57834
|
+
],
|
|
57835
|
+
"Supplier Quotation": [
|
|
57836
|
+
{ table: "items", fields: SUPPLIER_QUOTATION_ITEM_FIELDS }
|
|
57837
|
+
]
|
|
57838
|
+
};
|
|
57839
|
+
var BUY_DOCTYPE_FIELD_SPECS = {
|
|
57840
|
+
Item: ITEM_FIELDS,
|
|
57841
|
+
BOM: BOM_FIELDS,
|
|
57842
|
+
"Item Price": ITEM_PRICE_FIELDS,
|
|
57843
|
+
"Supplier Quotation": SUPPLIER_QUOTATION_FIELDS,
|
|
57844
|
+
Supplier: SUPPLIER_FIELDS,
|
|
57845
|
+
"Price List": PRICE_LIST_FIELDS,
|
|
57846
|
+
UOM: UOM_FIELDS,
|
|
57847
|
+
"Currency Exchange": CURRENCY_EXCHANGE_FIELDS
|
|
57848
|
+
};
|
|
57849
|
+
function isBuyCaptureDoctype(value) {
|
|
57850
|
+
return BUY_CAPTURE_DOCTYPE.includes(value);
|
|
57851
|
+
}
|
|
57852
|
+
async function projectBuyDocument(doctype, raw2) {
|
|
57853
|
+
const name = requiredName(raw2.name, `${doctype}.name`);
|
|
57854
|
+
const modified = requiredModified(raw2.modified, `${doctype}.modified`);
|
|
57855
|
+
const fields = projectFields(
|
|
57856
|
+
raw2,
|
|
57857
|
+
BUY_DOCTYPE_FIELD_SPECS[doctype],
|
|
57858
|
+
`${doctype}`
|
|
57859
|
+
);
|
|
57860
|
+
const children = projectChildren(doctype, raw2);
|
|
57861
|
+
const docstatus = optionalDocstatus(raw2.docstatus, `${doctype}.docstatus`);
|
|
57862
|
+
const status = optionalStatus(raw2.status, `${doctype}.status`);
|
|
57863
|
+
const identity = {
|
|
57864
|
+
doctype,
|
|
57865
|
+
name,
|
|
57866
|
+
modified,
|
|
57867
|
+
...docstatus !== void 0 ? { docstatus } : {},
|
|
57868
|
+
...status !== void 0 ? { status } : {},
|
|
57869
|
+
sourceCategory: BUY_DOCTYPE_SOURCE_CATEGORY[doctype],
|
|
57870
|
+
fields,
|
|
57871
|
+
...children !== void 0 ? { children } : {}
|
|
57872
|
+
};
|
|
57873
|
+
const fingerprint2 = await sha256FingerprintOfUtf8(
|
|
57874
|
+
canonicalJson2(identity, `${doctype}:${name} projection`)
|
|
57875
|
+
);
|
|
57876
|
+
return { ...identity, fingerprint: fingerprint2 };
|
|
57877
|
+
}
|
|
57878
|
+
function projectChildren(doctype, raw2) {
|
|
57879
|
+
const specs = BUY_DOCTYPE_CHILD_TABLES[doctype];
|
|
57880
|
+
if (!specs) return void 0;
|
|
57881
|
+
const tables = [];
|
|
57882
|
+
for (const spec of specs) {
|
|
57883
|
+
const value = raw2[spec.table];
|
|
57884
|
+
if (value === void 0 || value === null) continue;
|
|
57885
|
+
if (!Array.isArray(value)) {
|
|
57886
|
+
throw new BuyCaptureError(
|
|
57887
|
+
"BUY_CAPTURE_INVALID_PROJECTION",
|
|
57888
|
+
`${doctype}.${spec.table} must be a child table array.`,
|
|
57889
|
+
{
|
|
57890
|
+
recovery: "Capture only closed child tables with exact row identities.",
|
|
57891
|
+
context: { doctype, table: spec.table }
|
|
57892
|
+
}
|
|
57893
|
+
);
|
|
57894
|
+
}
|
|
57895
|
+
if (value.length > BUY_CAPTURE_MAX_CHILD_ROWS) {
|
|
57896
|
+
throw new BuyCaptureError(
|
|
57897
|
+
"BUY_CAPTURE_BOUNDS_EXCEEDED",
|
|
57898
|
+
`${doctype}.${spec.table} exceeds the ${BUY_CAPTURE_MAX_CHILD_ROWS}-row bound.`,
|
|
57899
|
+
{
|
|
57900
|
+
recovery: "Narrow the capture to fewer child rows.",
|
|
57901
|
+
context: {
|
|
57902
|
+
doctype,
|
|
57903
|
+
table: spec.table,
|
|
57904
|
+
limit: BUY_CAPTURE_MAX_CHILD_ROWS,
|
|
57905
|
+
actual: value.length
|
|
57906
|
+
}
|
|
57907
|
+
}
|
|
57908
|
+
);
|
|
57909
|
+
}
|
|
57910
|
+
if (value.length === 0) continue;
|
|
57911
|
+
const rows = value.map(
|
|
57912
|
+
(row, index) => projectChildRow(
|
|
57913
|
+
row,
|
|
57914
|
+
spec.fields,
|
|
57915
|
+
`${doctype}.${spec.table}[${index}]`
|
|
57916
|
+
)
|
|
57917
|
+
);
|
|
57918
|
+
tables.push({ table: spec.table, rows });
|
|
57919
|
+
}
|
|
57920
|
+
return tables.length > 0 ? tables : void 0;
|
|
57921
|
+
}
|
|
57922
|
+
function projectChildRow(row, specs, path) {
|
|
57923
|
+
if (typeof row !== "object" || row === null || Array.isArray(row)) {
|
|
57924
|
+
throw new BuyCaptureError(
|
|
57925
|
+
"BUY_CAPTURE_INVALID_PROJECTION",
|
|
57926
|
+
`${path} must be a child row object.`,
|
|
57927
|
+
{
|
|
57928
|
+
recovery: "Child rows must keep their exact Frappe name identity.",
|
|
57929
|
+
context: { path }
|
|
57930
|
+
}
|
|
57931
|
+
);
|
|
57932
|
+
}
|
|
57933
|
+
const record3 = row;
|
|
57934
|
+
const name = requiredName(record3.name, `${path}.name`);
|
|
57935
|
+
const idx = optionalIdx(record3.idx, `${path}.idx`);
|
|
57936
|
+
return {
|
|
57937
|
+
name,
|
|
57938
|
+
...idx !== void 0 ? { idx } : {},
|
|
57939
|
+
fields: projectFields(record3, specs, path)
|
|
57940
|
+
};
|
|
57941
|
+
}
|
|
57942
|
+
function projectFields(raw2, specs, path) {
|
|
57943
|
+
for (const key of Object.keys(raw2)) {
|
|
57944
|
+
if (SENSITIVE_FIELDS.has(key)) {
|
|
57945
|
+
continue;
|
|
57946
|
+
}
|
|
57947
|
+
}
|
|
57948
|
+
const fields = {};
|
|
57949
|
+
for (const spec of specs) {
|
|
57950
|
+
const value = projectField(raw2[spec.key], spec, `${path}.${spec.key}`);
|
|
57951
|
+
if (value !== void 0) fields[spec.key] = value;
|
|
57952
|
+
}
|
|
57953
|
+
return fields;
|
|
57954
|
+
}
|
|
57955
|
+
function projectField(value, spec, path) {
|
|
57956
|
+
if (value === void 0 || value === null || value === "") {
|
|
57957
|
+
if (spec.required) {
|
|
57958
|
+
throw new BuyCaptureError(
|
|
57959
|
+
"BUY_CAPTURE_INVALID_PROJECTION",
|
|
57960
|
+
`${path} is required on the closed commercial projection.`,
|
|
57961
|
+
{
|
|
57962
|
+
recovery: "The ERP document is missing a field later consumed as cost evidence.",
|
|
57963
|
+
context: { path, field: spec.key }
|
|
57964
|
+
}
|
|
57965
|
+
);
|
|
57966
|
+
}
|
|
57967
|
+
return void 0;
|
|
57968
|
+
}
|
|
57969
|
+
switch (spec.kind) {
|
|
57970
|
+
case "string":
|
|
57971
|
+
return requiredString(value, path);
|
|
57972
|
+
case "decimal":
|
|
57973
|
+
return requiredDecimal(value, path);
|
|
57974
|
+
case "boolean":
|
|
57975
|
+
return requiredBoolean(value, path);
|
|
57976
|
+
case "currency":
|
|
57977
|
+
return requiredCurrency(value, path);
|
|
57978
|
+
case "date":
|
|
57979
|
+
return requiredDate(value, path);
|
|
57980
|
+
case "integer":
|
|
57981
|
+
return requiredInteger(value, path);
|
|
57982
|
+
}
|
|
57983
|
+
}
|
|
57984
|
+
function requiredName(value, path) {
|
|
57985
|
+
const name = requiredString(value, path);
|
|
57986
|
+
if (name.length > BUY_CAPTURE_MAX_NAME_LENGTH) {
|
|
57987
|
+
throw new BuyCaptureError(
|
|
57988
|
+
"BUY_CAPTURE_BOUNDS_EXCEEDED",
|
|
57989
|
+
`${path} exceeds the ${BUY_CAPTURE_MAX_NAME_LENGTH}-character bound.`,
|
|
57990
|
+
{
|
|
57991
|
+
recovery: "Use the exact ERP document name; do not pass a description.",
|
|
57992
|
+
context: {
|
|
57993
|
+
path,
|
|
57994
|
+
limit: BUY_CAPTURE_MAX_NAME_LENGTH,
|
|
57995
|
+
actual: name.length
|
|
57996
|
+
}
|
|
57997
|
+
}
|
|
57998
|
+
);
|
|
57999
|
+
}
|
|
58000
|
+
return name;
|
|
58001
|
+
}
|
|
58002
|
+
function requiredModified(value, path) {
|
|
58003
|
+
try {
|
|
58004
|
+
return frappeDatetime(value, path);
|
|
58005
|
+
} catch {
|
|
58006
|
+
throw new BuyCaptureError(
|
|
58007
|
+
"BUY_CAPTURE_INVALID_PROJECTION",
|
|
58008
|
+
`${path} is not a Frappe datetime.`,
|
|
58009
|
+
{
|
|
58010
|
+
recovery: "Capture the document's exact modified timestamp.",
|
|
58011
|
+
context: { path }
|
|
58012
|
+
}
|
|
58013
|
+
);
|
|
58014
|
+
}
|
|
58015
|
+
}
|
|
58016
|
+
function requiredString(value, path) {
|
|
58017
|
+
if (typeof value !== "string" || value.trim() === "" || value.trim() !== value) {
|
|
58018
|
+
throw new BuyCaptureError(
|
|
58019
|
+
"BUY_CAPTURE_INVALID_PROJECTION",
|
|
58020
|
+
`${path} must be a non-empty unpadded string.`,
|
|
58021
|
+
{
|
|
58022
|
+
recovery: "Use the exact ERP string; do not invent a label.",
|
|
58023
|
+
context: { path }
|
|
58024
|
+
}
|
|
58025
|
+
);
|
|
58026
|
+
}
|
|
58027
|
+
return value;
|
|
58028
|
+
}
|
|
58029
|
+
function requiredDecimal(value, path) {
|
|
58030
|
+
if (typeof value === "number") {
|
|
58031
|
+
if (!Number.isFinite(value)) {
|
|
58032
|
+
throw new BuyCaptureError(
|
|
58033
|
+
"BUY_CAPTURE_INVALID_PROJECTION",
|
|
58034
|
+
`${path} must be a finite decimal.`,
|
|
58035
|
+
{
|
|
58036
|
+
recovery: "Refuse non-finite commercial amounts rather than coerce them.",
|
|
58037
|
+
context: { path }
|
|
58038
|
+
}
|
|
58039
|
+
);
|
|
58040
|
+
}
|
|
58041
|
+
return JSON.stringify(value);
|
|
58042
|
+
}
|
|
58043
|
+
if (typeof value === "string") {
|
|
58044
|
+
if (!DECIMAL.test(value)) {
|
|
58045
|
+
throw new BuyCaptureError(
|
|
58046
|
+
"BUY_CAPTURE_INVALID_PROJECTION",
|
|
58047
|
+
`${path} must be a canonical decimal string.`,
|
|
58048
|
+
{
|
|
58049
|
+
recovery: "Keep the ERP decimal; do not fill absence with zero.",
|
|
58050
|
+
context: { path }
|
|
58051
|
+
}
|
|
58052
|
+
);
|
|
58053
|
+
}
|
|
58054
|
+
return value;
|
|
58055
|
+
}
|
|
58056
|
+
throw new BuyCaptureError(
|
|
58057
|
+
"BUY_CAPTURE_INVALID_PROJECTION",
|
|
58058
|
+
`${path} must be a decimal.`,
|
|
58059
|
+
{
|
|
58060
|
+
recovery: "Keep the ERP decimal; do not fill absence with zero.",
|
|
58061
|
+
context: { path }
|
|
58062
|
+
}
|
|
58063
|
+
);
|
|
58064
|
+
}
|
|
58065
|
+
function requiredBoolean(value, path) {
|
|
58066
|
+
if (value === true || value === 1) return true;
|
|
58067
|
+
if (value === false || value === 0) return false;
|
|
58068
|
+
throw new BuyCaptureError(
|
|
58069
|
+
"BUY_CAPTURE_INVALID_PROJECTION",
|
|
58070
|
+
`${path} must be a boolean or Frappe 0/1.`,
|
|
58071
|
+
{
|
|
58072
|
+
recovery: "Do not coerce missing flags to false.",
|
|
58073
|
+
context: { path }
|
|
58074
|
+
}
|
|
58075
|
+
);
|
|
58076
|
+
}
|
|
58077
|
+
function requiredCurrency(value, path) {
|
|
58078
|
+
const code = requiredString(value, path);
|
|
58079
|
+
if (!CURRENCY.test(code)) {
|
|
58080
|
+
throw new BuyCaptureError(
|
|
58081
|
+
"BUY_CAPTURE_INVALID_PROJECTION",
|
|
58082
|
+
`${path} must be a 3-letter currency code.`,
|
|
58083
|
+
{
|
|
58084
|
+
recovery: "Capture the document currency; do not guess a conversion.",
|
|
58085
|
+
context: { path }
|
|
58086
|
+
}
|
|
58087
|
+
);
|
|
58088
|
+
}
|
|
58089
|
+
return code;
|
|
58090
|
+
}
|
|
58091
|
+
function requiredDate(value, path) {
|
|
58092
|
+
try {
|
|
58093
|
+
return calendarDate(value, path);
|
|
58094
|
+
} catch {
|
|
58095
|
+
throw new BuyCaptureError(
|
|
58096
|
+
"BUY_CAPTURE_INVALID_PROJECTION",
|
|
58097
|
+
`${path} must be a real YYYY-MM-DD date.`,
|
|
58098
|
+
{
|
|
58099
|
+
recovery: "Keep the ERP calendar date; do not localize it.",
|
|
58100
|
+
context: { path }
|
|
58101
|
+
}
|
|
58102
|
+
);
|
|
58103
|
+
}
|
|
58104
|
+
}
|
|
58105
|
+
function requiredInteger(value, path) {
|
|
58106
|
+
if (!Number.isSafeInteger(value)) {
|
|
58107
|
+
throw new BuyCaptureError(
|
|
58108
|
+
"BUY_CAPTURE_INVALID_PROJECTION",
|
|
58109
|
+
`${path} must be a safe integer.`,
|
|
58110
|
+
{ recovery: "Keep the ERP integer identity.", context: { path } }
|
|
58111
|
+
);
|
|
58112
|
+
}
|
|
58113
|
+
return value;
|
|
58114
|
+
}
|
|
58115
|
+
function optionalDocstatus(value, path) {
|
|
58116
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
58117
|
+
if (value !== 0 && value !== 1 && value !== 2) {
|
|
58118
|
+
throw new BuyCaptureError(
|
|
58119
|
+
"BUY_CAPTURE_INVALID_PROJECTION",
|
|
58120
|
+
`${path} must be 0, 1, or 2.`,
|
|
58121
|
+
{ recovery: "Keep the ERP docstatus.", context: { path } }
|
|
58122
|
+
);
|
|
58123
|
+
}
|
|
58124
|
+
return value;
|
|
58125
|
+
}
|
|
58126
|
+
function optionalStatus(value, path) {
|
|
58127
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
58128
|
+
return requiredString(value, path);
|
|
58129
|
+
}
|
|
58130
|
+
function optionalIdx(value, path) {
|
|
58131
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
58132
|
+
return requiredInteger(value, path);
|
|
58133
|
+
}
|
|
58134
|
+
function parseClosedWireFields(value, specs, path) {
|
|
58135
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
58136
|
+
throw new TypeError(`${path} must be an object.`);
|
|
58137
|
+
}
|
|
58138
|
+
const root = value;
|
|
58139
|
+
const allowed = new Map(specs.map((spec) => [spec.key, spec]));
|
|
58140
|
+
for (const key of Object.keys(root)) {
|
|
58141
|
+
if (!allowed.has(key)) {
|
|
58142
|
+
throw new TypeError(`${path}.${key} is not a closed commercial field.`);
|
|
58143
|
+
}
|
|
58144
|
+
}
|
|
58145
|
+
const fields = {};
|
|
58146
|
+
for (const spec of specs) {
|
|
58147
|
+
const raw2 = root[spec.key];
|
|
58148
|
+
if (raw2 === void 0 || raw2 === null || raw2 === "") {
|
|
58149
|
+
if (spec.required) {
|
|
58150
|
+
throw new TypeError(`${path}.${spec.key} is required.`);
|
|
58151
|
+
}
|
|
58152
|
+
continue;
|
|
58153
|
+
}
|
|
58154
|
+
fields[spec.key] = parseWireValue(raw2, spec, `${path}.${spec.key}`);
|
|
58155
|
+
}
|
|
58156
|
+
return fields;
|
|
58157
|
+
}
|
|
58158
|
+
function parseClosedWireChildren(doctype, value, path) {
|
|
58159
|
+
if (value === void 0) return void 0;
|
|
58160
|
+
if (!Array.isArray(value)) {
|
|
58161
|
+
throw new TypeError(`${path} must be an array.`);
|
|
58162
|
+
}
|
|
58163
|
+
const specs = BUY_DOCTYPE_CHILD_TABLES[doctype] ?? [];
|
|
58164
|
+
if (specs.length === 0) {
|
|
58165
|
+
throw new TypeError(`${path} is not admitted for ${doctype}.`);
|
|
58166
|
+
}
|
|
58167
|
+
if (value.length === 0) {
|
|
58168
|
+
throw new TypeError(`${path} must be omitted when empty.`);
|
|
58169
|
+
}
|
|
58170
|
+
const known = new Map(specs.map((spec) => [spec.table, spec]));
|
|
58171
|
+
const seenTables = /* @__PURE__ */ new Set();
|
|
58172
|
+
return value.map((item, index) => {
|
|
58173
|
+
const tablePath = `${path}[${index}]`;
|
|
58174
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) {
|
|
58175
|
+
throw new TypeError(`${tablePath} must be an object.`);
|
|
58176
|
+
}
|
|
58177
|
+
const table2 = item;
|
|
58178
|
+
const keys = Object.keys(table2).toSorted();
|
|
58179
|
+
if (keys.length !== 2 || keys[0] !== "rows" || keys[1] !== "table") {
|
|
58180
|
+
throw new TypeError(
|
|
58181
|
+
`${tablePath} contains missing or unsupported fields.`
|
|
58182
|
+
);
|
|
58183
|
+
}
|
|
58184
|
+
if (typeof table2.table !== "string" || !known.has(table2.table)) {
|
|
58185
|
+
throw new TypeError(
|
|
58186
|
+
`${tablePath}.table is not a closed child table for ${doctype}.`
|
|
58187
|
+
);
|
|
58188
|
+
}
|
|
58189
|
+
if (seenTables.has(table2.table)) {
|
|
58190
|
+
throw new TypeError(`${tablePath}.table duplicates ${table2.table}.`);
|
|
58191
|
+
}
|
|
58192
|
+
seenTables.add(table2.table);
|
|
58193
|
+
const spec = known.get(table2.table);
|
|
58194
|
+
if (!Array.isArray(table2.rows)) {
|
|
58195
|
+
throw new TypeError(`${tablePath}.rows must be an array.`);
|
|
58196
|
+
}
|
|
58197
|
+
if (table2.rows.length > BUY_CAPTURE_MAX_CHILD_ROWS) {
|
|
58198
|
+
throw new TypeError(
|
|
58199
|
+
`${tablePath}.rows exceeds the ${BUY_CAPTURE_MAX_CHILD_ROWS}-row bound.`
|
|
58200
|
+
);
|
|
58201
|
+
}
|
|
58202
|
+
const seenRows = /* @__PURE__ */ new Set();
|
|
58203
|
+
const rows = table2.rows.map((row, rowIndex) => {
|
|
58204
|
+
const rowPath = `${tablePath}.rows[${rowIndex}]`;
|
|
58205
|
+
const parsed = parseClosedWireRow(row, spec.fields, rowPath);
|
|
58206
|
+
if (seenRows.has(parsed.name)) {
|
|
58207
|
+
throw new TypeError(`${rowPath} duplicates child row ${parsed.name}.`);
|
|
58208
|
+
}
|
|
58209
|
+
seenRows.add(parsed.name);
|
|
58210
|
+
return parsed;
|
|
58211
|
+
});
|
|
58212
|
+
return { table: spec.table, rows };
|
|
58213
|
+
});
|
|
58214
|
+
}
|
|
58215
|
+
function parseClosedWireRow(value, specs, path) {
|
|
58216
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
58217
|
+
throw new TypeError(`${path} must be an object.`);
|
|
58218
|
+
}
|
|
58219
|
+
const root = value;
|
|
58220
|
+
const keys = Object.keys(root).toSorted();
|
|
58221
|
+
const allowed = root.idx === void 0 ? ["fields", "name"] : ["fields", "idx", "name"];
|
|
58222
|
+
if (keys.length !== allowed.length || keys.some((key, index) => key !== allowed[index])) {
|
|
58223
|
+
throw new TypeError(`${path} contains missing or unsupported fields.`);
|
|
58224
|
+
}
|
|
58225
|
+
const name = requiredWireName(root.name, `${path}.name`);
|
|
58226
|
+
return {
|
|
58227
|
+
name,
|
|
58228
|
+
...root.idx === void 0 ? {} : {
|
|
58229
|
+
idx: requiredWireIdx(root.idx, `${path}.idx`)
|
|
58230
|
+
},
|
|
58231
|
+
fields: parseClosedWireFields(root.fields, specs, `${path}.fields`)
|
|
58232
|
+
};
|
|
58233
|
+
}
|
|
58234
|
+
function parseWireValue(value, spec, path) {
|
|
58235
|
+
switch (spec.kind) {
|
|
58236
|
+
case "string":
|
|
58237
|
+
if (typeof value !== "string" || value.trim() === "" || value.trim() !== value) {
|
|
58238
|
+
throw new TypeError(`${path} must be a non-empty unpadded string.`);
|
|
58239
|
+
}
|
|
58240
|
+
return value;
|
|
58241
|
+
case "decimal":
|
|
58242
|
+
if (typeof value !== "string" || !DECIMAL.test(value)) {
|
|
58243
|
+
throw new TypeError(`${path} must be a canonical decimal string.`);
|
|
58244
|
+
}
|
|
58245
|
+
return value;
|
|
58246
|
+
case "boolean":
|
|
58247
|
+
if (value !== true && value !== false) {
|
|
58248
|
+
throw new TypeError(`${path} must be a boolean.`);
|
|
58249
|
+
}
|
|
58250
|
+
return value;
|
|
58251
|
+
case "currency":
|
|
58252
|
+
if (typeof value !== "string" || !CURRENCY.test(value)) {
|
|
58253
|
+
throw new TypeError(`${path} must be a 3-letter currency code.`);
|
|
58254
|
+
}
|
|
58255
|
+
return value;
|
|
58256
|
+
case "date":
|
|
58257
|
+
return calendarDate(value, path);
|
|
58258
|
+
case "integer":
|
|
58259
|
+
if (!Number.isSafeInteger(value)) {
|
|
58260
|
+
throw new TypeError(`${path} must be a safe integer.`);
|
|
58261
|
+
}
|
|
58262
|
+
return value;
|
|
58263
|
+
}
|
|
58264
|
+
}
|
|
58265
|
+
function requiredWireName(value, path) {
|
|
58266
|
+
if (typeof value !== "string" || value.trim() === "" || value.trim() !== value) {
|
|
58267
|
+
throw new TypeError(`${path} must be a non-empty unpadded string.`);
|
|
58268
|
+
}
|
|
58269
|
+
if (value.length > BUY_CAPTURE_MAX_NAME_LENGTH) {
|
|
58270
|
+
throw new TypeError(
|
|
58271
|
+
`${path} exceeds the ${BUY_CAPTURE_MAX_NAME_LENGTH}-character bound.`
|
|
58272
|
+
);
|
|
58273
|
+
}
|
|
58274
|
+
return value;
|
|
58275
|
+
}
|
|
58276
|
+
function requiredWireIdx(value, path) {
|
|
58277
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
58278
|
+
throw new TypeError(`${path} must be a non-negative integer.`);
|
|
58279
|
+
}
|
|
58280
|
+
return value;
|
|
58281
|
+
}
|
|
58282
|
+
|
|
58283
|
+
// src/buy/site.ts
|
|
58284
|
+
async function sourceInstanceFromClient(client) {
|
|
58285
|
+
const normalized = client.normalizedSiteUrl();
|
|
58286
|
+
return {
|
|
58287
|
+
kind: BUY_SOURCE_INSTANCE_KIND,
|
|
58288
|
+
siteId: await sha256FingerprintOfUtf8(normalized)
|
|
58289
|
+
};
|
|
58290
|
+
}
|
|
58291
|
+
|
|
58292
|
+
// src/buy/capture.ts
|
|
58293
|
+
var CALLER_AUTHORITY_KEYS = [
|
|
58294
|
+
"sourceInstance",
|
|
58295
|
+
"capturedAt",
|
|
58296
|
+
"fingerprint",
|
|
58297
|
+
"canonicalText",
|
|
58298
|
+
"byteCount",
|
|
58299
|
+
"endpoint",
|
|
58300
|
+
"uri",
|
|
58301
|
+
"credentials",
|
|
58302
|
+
"baseUrl",
|
|
58303
|
+
"siteId",
|
|
58304
|
+
"attestedTimestamp",
|
|
58305
|
+
"digest"
|
|
58306
|
+
];
|
|
58307
|
+
function parseBuyCaptureInput(value) {
|
|
58308
|
+
const root = asRecord(value, "buy capture input");
|
|
58309
|
+
for (const key of CALLER_AUTHORITY_KEYS) {
|
|
58310
|
+
if (key in root) {
|
|
58311
|
+
throw new BuyCaptureError(
|
|
58312
|
+
"BUY_CAPTURE_CALLER_AUTHORITY_REJECTED",
|
|
58313
|
+
`buy capture input must not include caller-supplied ${key}.`,
|
|
58314
|
+
{
|
|
58315
|
+
recovery: "Omit sourceInstance, capturedAt, fingerprints, endpoints, and credentials; the configured FrappeClient and server clock own those fields.",
|
|
58316
|
+
context: { key }
|
|
58317
|
+
}
|
|
58318
|
+
);
|
|
58319
|
+
}
|
|
58320
|
+
}
|
|
58321
|
+
const keys = Object.keys(root);
|
|
58322
|
+
if (keys.length !== 1 || keys[0] !== "documents") {
|
|
58323
|
+
throw new BuyCaptureError(
|
|
58324
|
+
"BUY_CAPTURE_INVALID_INPUT",
|
|
58325
|
+
"buy capture input must contain only documents.",
|
|
58326
|
+
{
|
|
58327
|
+
recovery: "Pass a bounded list of exact {doctype, name, expectedModified?} references.",
|
|
58328
|
+
context: { keys }
|
|
58329
|
+
}
|
|
58330
|
+
);
|
|
58331
|
+
}
|
|
58332
|
+
if (!Array.isArray(root.documents)) {
|
|
58333
|
+
throw new BuyCaptureError(
|
|
58334
|
+
"BUY_CAPTURE_INVALID_INPUT",
|
|
58335
|
+
"buy capture input.documents must be an array.",
|
|
58336
|
+
{
|
|
58337
|
+
recovery: "Pass a dense list of exact document references."
|
|
58338
|
+
}
|
|
58339
|
+
);
|
|
58340
|
+
}
|
|
58341
|
+
if (root.documents.length === 0) {
|
|
58342
|
+
throw new BuyCaptureError(
|
|
58343
|
+
"BUY_CAPTURE_INVALID_INPUT",
|
|
58344
|
+
"buy capture input.documents must not be empty.",
|
|
58345
|
+
{
|
|
58346
|
+
recovery: "Name the exact documents to capture."
|
|
58347
|
+
}
|
|
58348
|
+
);
|
|
58349
|
+
}
|
|
58350
|
+
if (root.documents.length > BUY_CAPTURE_MAX_DOCUMENTS) {
|
|
58351
|
+
throw new BuyCaptureError(
|
|
58352
|
+
"BUY_CAPTURE_BOUNDS_EXCEEDED",
|
|
58353
|
+
`buy capture input.documents exceeds the ${BUY_CAPTURE_MAX_DOCUMENTS}-document bound.`,
|
|
58354
|
+
{
|
|
58355
|
+
recovery: "Narrow the capture; silent pagination is not allowed.",
|
|
58356
|
+
context: {
|
|
58357
|
+
limit: BUY_CAPTURE_MAX_DOCUMENTS,
|
|
58358
|
+
actual: root.documents.length
|
|
58359
|
+
}
|
|
58360
|
+
}
|
|
58361
|
+
);
|
|
58362
|
+
}
|
|
58363
|
+
const seen = /* @__PURE__ */ new Set();
|
|
58364
|
+
const documents = root.documents.map(
|
|
58365
|
+
(item, index) => parseDocumentRef(item, index, seen)
|
|
58366
|
+
);
|
|
58367
|
+
return { documents };
|
|
58368
|
+
}
|
|
58369
|
+
async function parseBuySourceCapture(value) {
|
|
58370
|
+
const root = exactRecord(value, [
|
|
58371
|
+
"schemaVersion",
|
|
58372
|
+
"sourceInstance",
|
|
58373
|
+
"capturedAt",
|
|
58374
|
+
"documents",
|
|
58375
|
+
"consistency"
|
|
58376
|
+
], "buy source capture");
|
|
58377
|
+
literal2(
|
|
58378
|
+
root.schemaVersion,
|
|
58379
|
+
BUY_SOURCE_CAPTURE_SCHEMA,
|
|
58380
|
+
"buy source capture.schemaVersion"
|
|
58381
|
+
);
|
|
58382
|
+
const source = exactRecord(
|
|
58383
|
+
root.sourceInstance,
|
|
58384
|
+
["kind", "siteId"],
|
|
58385
|
+
"buy source capture.sourceInstance"
|
|
58386
|
+
);
|
|
58387
|
+
literal2(
|
|
58388
|
+
source.kind,
|
|
58389
|
+
BUY_SOURCE_INSTANCE_KIND,
|
|
58390
|
+
"buy source capture.sourceInstance.kind"
|
|
58391
|
+
);
|
|
58392
|
+
const documents = [];
|
|
58393
|
+
const rawDocuments = boundedArray(
|
|
58394
|
+
root.documents,
|
|
58395
|
+
BUY_CAPTURE_MAX_DOCUMENTS,
|
|
58396
|
+
"buy source capture.documents"
|
|
58397
|
+
);
|
|
58398
|
+
if (rawDocuments.length === 0) {
|
|
58399
|
+
throw new TypeError("buy source capture.documents must not be empty.");
|
|
58400
|
+
}
|
|
58401
|
+
const seenDocuments = /* @__PURE__ */ new Set();
|
|
58402
|
+
for (let index = 0; index < rawDocuments.length; index += 1) {
|
|
58403
|
+
const parsed = await parseCapturedDocument(rawDocuments[index], index);
|
|
58404
|
+
const id = `${parsed.doctype}:${parsed.name}`;
|
|
58405
|
+
if (seenDocuments.has(id)) {
|
|
58406
|
+
throw new TypeError(
|
|
58407
|
+
`buy source capture.documents[${index}] duplicates ${id}.`
|
|
58408
|
+
);
|
|
58409
|
+
}
|
|
58410
|
+
seenDocuments.add(id);
|
|
58411
|
+
documents.push(parsed);
|
|
58412
|
+
}
|
|
58413
|
+
const consistency = exactRecord(
|
|
58414
|
+
root.consistency,
|
|
58415
|
+
["kind", "reads", "consistent"],
|
|
58416
|
+
"buy source capture.consistency"
|
|
58417
|
+
);
|
|
58418
|
+
literal2(
|
|
58419
|
+
consistency.kind,
|
|
58420
|
+
BUY_CONSISTENCY_KIND,
|
|
58421
|
+
"buy source capture.consistency.kind"
|
|
58422
|
+
);
|
|
58423
|
+
if (consistency.reads !== BUY_CONSISTENCY_READS) {
|
|
58424
|
+
throw new TypeError(
|
|
58425
|
+
"buy source capture.consistency.reads must be 2."
|
|
58426
|
+
);
|
|
58427
|
+
}
|
|
58428
|
+
if (consistency.consistent !== true) {
|
|
58429
|
+
throw new TypeError(
|
|
58430
|
+
"buy source capture.consistency.consistent must be true; inconsistent captures are not a usable payload."
|
|
58431
|
+
);
|
|
58432
|
+
}
|
|
58433
|
+
return {
|
|
58434
|
+
schemaVersion: BUY_SOURCE_CAPTURE_SCHEMA,
|
|
58435
|
+
sourceInstance: {
|
|
58436
|
+
kind: BUY_SOURCE_INSTANCE_KIND,
|
|
58437
|
+
siteId: fingerprint(
|
|
58438
|
+
source.siteId,
|
|
58439
|
+
"buy source capture.sourceInstance.siteId"
|
|
58440
|
+
)
|
|
58441
|
+
},
|
|
58442
|
+
capturedAt: canonicalTimestamp(
|
|
58443
|
+
root.capturedAt,
|
|
58444
|
+
"buy source capture.capturedAt"
|
|
58445
|
+
),
|
|
58446
|
+
documents,
|
|
58447
|
+
consistency: {
|
|
58448
|
+
kind: BUY_CONSISTENCY_KIND,
|
|
58449
|
+
reads: BUY_CONSISTENCY_READS,
|
|
58450
|
+
consistent: true
|
|
58451
|
+
}
|
|
58452
|
+
};
|
|
58453
|
+
}
|
|
58454
|
+
async function sealBuySourceCapture(capture) {
|
|
58455
|
+
const parsed = await parseBuySourceCapture(capture);
|
|
58456
|
+
const canonicalText = canonicalJson2(parsed, "buy source capture");
|
|
58457
|
+
const byteCount = utf8ByteCount(canonicalText);
|
|
58458
|
+
if (byteCount > BUY_CAPTURE_MAX_CANONICAL_BYTES) {
|
|
58459
|
+
throw new BuyCaptureError(
|
|
58460
|
+
"BUY_CAPTURE_BOUNDS_EXCEEDED",
|
|
58461
|
+
`buy source capture exceeds the ${BUY_CAPTURE_MAX_CANONICAL_BYTES}-byte bound.`,
|
|
58462
|
+
{
|
|
58463
|
+
recovery: "Narrow the capture; silent pagination is not allowed.",
|
|
58464
|
+
context: { limit: BUY_CAPTURE_MAX_CANONICAL_BYTES, actual: byteCount }
|
|
58465
|
+
}
|
|
58466
|
+
);
|
|
58467
|
+
}
|
|
58468
|
+
return {
|
|
58469
|
+
schemaVersion: BUY_SOURCE_CAPTURE_SCHEMA,
|
|
58470
|
+
capture: parsed,
|
|
58471
|
+
canonicalText,
|
|
58472
|
+
fingerprint: await sha256FingerprintOfUtf8(canonicalText),
|
|
58473
|
+
byteCount
|
|
58474
|
+
};
|
|
58475
|
+
}
|
|
58476
|
+
async function runBuyCapture(input, deps) {
|
|
58477
|
+
const parsedInput = parseBuyCaptureInput(input);
|
|
58478
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
58479
|
+
const capturedAt = canonicalTimestamp(
|
|
58480
|
+
now().toISOString(),
|
|
58481
|
+
"buy source capture.capturedAt"
|
|
58482
|
+
);
|
|
58483
|
+
const sourceInstance = await sourceInstanceFromClient(deps.client);
|
|
58484
|
+
const first = await readAll(parsedInput.documents, deps.client);
|
|
58485
|
+
const documents = await Promise.all(
|
|
58486
|
+
first.map((row) => projectBuyDocument(row.doctype, row.doc))
|
|
58487
|
+
);
|
|
58488
|
+
for (let index = 0; index < parsedInput.documents.length; index += 1) {
|
|
58489
|
+
const expected = parsedInput.documents[index].expectedModified;
|
|
58490
|
+
if (expected !== void 0 && documents[index].modified !== expected) {
|
|
58491
|
+
throw new BuyCaptureError(
|
|
58492
|
+
"BUY_CAPTURE_EXPECTED_MODIFIED_MISMATCH",
|
|
58493
|
+
`Document ${documents[index].doctype}:${documents[index].name} modified does not match expectedModified.`,
|
|
58494
|
+
{
|
|
58495
|
+
recovery: "Re-resolve the exact document revision before capturing.",
|
|
58496
|
+
context: {
|
|
58497
|
+
doctype: documents[index].doctype,
|
|
58498
|
+
name: documents[index].name,
|
|
58499
|
+
modified: documents[index].modified,
|
|
58500
|
+
expectedModified: expected
|
|
58501
|
+
}
|
|
58502
|
+
}
|
|
58503
|
+
);
|
|
58504
|
+
}
|
|
58505
|
+
}
|
|
58506
|
+
const second = await readAll(parsedInput.documents, deps.client);
|
|
58507
|
+
const reread = await Promise.all(
|
|
58508
|
+
second.map((row) => projectBuyDocument(row.doctype, row.doc))
|
|
58509
|
+
);
|
|
58510
|
+
for (let index = 0; index < documents.length; index += 1) {
|
|
58511
|
+
const a = documents[index];
|
|
58512
|
+
const b = reread[index];
|
|
58513
|
+
if (a.modified !== b.modified || a.fingerprint !== b.fingerprint) {
|
|
58514
|
+
throw new BuyCaptureError(
|
|
58515
|
+
"BUY_CAPTURE_INCONSISTENT",
|
|
58516
|
+
`Document ${a.doctype}:${a.name} changed between the two skipCache reads.`,
|
|
58517
|
+
{
|
|
58518
|
+
retryable: true,
|
|
58519
|
+
recovery: "Retry the capture. Repeated-read consistency failed; this is not a transactional snapshot.",
|
|
58520
|
+
context: {
|
|
58521
|
+
doctype: a.doctype,
|
|
58522
|
+
name: a.name,
|
|
58523
|
+
firstModified: a.modified,
|
|
58524
|
+
secondModified: b.modified,
|
|
58525
|
+
firstFingerprint: a.fingerprint,
|
|
58526
|
+
secondFingerprint: b.fingerprint
|
|
58527
|
+
}
|
|
58528
|
+
}
|
|
58529
|
+
);
|
|
58530
|
+
}
|
|
58531
|
+
}
|
|
58532
|
+
return await sealBuySourceCapture({
|
|
58533
|
+
schemaVersion: BUY_SOURCE_CAPTURE_SCHEMA,
|
|
58534
|
+
sourceInstance,
|
|
58535
|
+
capturedAt,
|
|
58536
|
+
documents,
|
|
58537
|
+
consistency: {
|
|
58538
|
+
kind: BUY_CONSISTENCY_KIND,
|
|
58539
|
+
reads: BUY_CONSISTENCY_READS,
|
|
58540
|
+
consistent: true
|
|
58541
|
+
}
|
|
58542
|
+
});
|
|
58543
|
+
}
|
|
58544
|
+
async function readAll(refs, client) {
|
|
58545
|
+
const docs = [];
|
|
58546
|
+
for (const ref of refs) {
|
|
58547
|
+
let doc;
|
|
58548
|
+
try {
|
|
58549
|
+
doc = await client.get(ref.doctype, ref.name, { skipCache: true });
|
|
58550
|
+
} catch (error2) {
|
|
58551
|
+
if (error2 instanceof FrappeAPIError && error2.status === 404) {
|
|
58552
|
+
throw new BuyCaptureError(
|
|
58553
|
+
"BUY_CAPTURE_MISSING_SOURCE",
|
|
58554
|
+
`Document ${ref.doctype}:${ref.name} was not found.`,
|
|
58555
|
+
{
|
|
58556
|
+
recovery: "Capture only documents that currently exist on the configured site.",
|
|
58557
|
+
context: { doctype: ref.doctype, name: ref.name }
|
|
58558
|
+
}
|
|
58559
|
+
);
|
|
58560
|
+
}
|
|
58561
|
+
throw error2;
|
|
58562
|
+
}
|
|
58563
|
+
assertExactReturnedIdentity(ref, doc);
|
|
58564
|
+
docs.push({ doctype: ref.doctype, doc });
|
|
58565
|
+
}
|
|
58566
|
+
return docs;
|
|
58567
|
+
}
|
|
58568
|
+
function assertExactReturnedIdentity(ref, doc) {
|
|
58569
|
+
if (doc.name !== ref.name) {
|
|
58570
|
+
throw new BuyCaptureError(
|
|
58571
|
+
"BUY_CAPTURE_IDENTITY_MISMATCH",
|
|
58572
|
+
`Requested ${ref.doctype}:${ref.name} but the client returned name ${String(doc.name)}.`,
|
|
58573
|
+
{
|
|
58574
|
+
recovery: "Refuse the response; do not relabel a different ERP document as the requested identity.",
|
|
58575
|
+
context: {
|
|
58576
|
+
requestedDoctype: ref.doctype,
|
|
58577
|
+
requestedName: ref.name,
|
|
58578
|
+
returnedName: doc.name
|
|
58579
|
+
}
|
|
58580
|
+
}
|
|
58581
|
+
);
|
|
58582
|
+
}
|
|
58583
|
+
if (doc.doctype !== void 0 && doc.doctype !== null && doc.doctype !== "" && doc.doctype !== ref.doctype) {
|
|
58584
|
+
throw new BuyCaptureError(
|
|
58585
|
+
"BUY_CAPTURE_IDENTITY_MISMATCH",
|
|
58586
|
+
`Requested ${ref.doctype}:${ref.name} but the client returned doctype ${String(doc.doctype)}.`,
|
|
58587
|
+
{
|
|
58588
|
+
recovery: "Refuse the response; do not relabel a different ERP document as the requested identity.",
|
|
58589
|
+
context: {
|
|
58590
|
+
requestedDoctype: ref.doctype,
|
|
58591
|
+
requestedName: ref.name,
|
|
58592
|
+
returnedDoctype: doc.doctype
|
|
58593
|
+
}
|
|
58594
|
+
}
|
|
58595
|
+
);
|
|
58596
|
+
}
|
|
58597
|
+
}
|
|
58598
|
+
function parseDocumentRef(value, index, seen) {
|
|
58599
|
+
const path = `buy capture input.documents[${index}]`;
|
|
58600
|
+
const root = asRecord(value, path);
|
|
58601
|
+
const keys = Object.keys(root).toSorted();
|
|
58602
|
+
const allowed = root.expectedModified === void 0 ? ["doctype", "name"] : ["doctype", "expectedModified", "name"];
|
|
58603
|
+
if (keys.length !== allowed.length || keys.some((key, keyIndex) => key !== allowed[keyIndex])) {
|
|
58604
|
+
throw new BuyCaptureError(
|
|
58605
|
+
"BUY_CAPTURE_INVALID_INPUT",
|
|
58606
|
+
`${path} must contain only doctype, name, and optional expectedModified.`,
|
|
58607
|
+
{
|
|
58608
|
+
recovery: "Do not pass endpoints, sourceInstance, or extra ERP fields.",
|
|
58609
|
+
context: { keys }
|
|
58610
|
+
}
|
|
58611
|
+
);
|
|
58612
|
+
}
|
|
58613
|
+
if (typeof root.doctype !== "string" || !isBuyCaptureDoctype(root.doctype)) {
|
|
58614
|
+
throw new BuyCaptureError(
|
|
58615
|
+
"BUY_CAPTURE_UNSUPPORTED_DOCTYPE",
|
|
58616
|
+
`${path}.doctype is not a closed Buy capture DocType.`,
|
|
58617
|
+
{
|
|
58618
|
+
recovery: `Use one of ${BUY_CAPTURE_DOCTYPE.join(", ")}. Generic doctypes cannot masquerade as a price.`,
|
|
58619
|
+
context: { doctype: root.doctype }
|
|
58620
|
+
}
|
|
58621
|
+
);
|
|
58622
|
+
}
|
|
58623
|
+
if (typeof root.name !== "string" || root.name.trim() === "" || root.name.trim() !== root.name) {
|
|
58624
|
+
throw new BuyCaptureError(
|
|
58625
|
+
"BUY_CAPTURE_INVALID_INPUT",
|
|
58626
|
+
`${path}.name must be the exact ERP document name.`,
|
|
58627
|
+
{ recovery: "Pass the exact document name, not a label." }
|
|
58628
|
+
);
|
|
58629
|
+
}
|
|
58630
|
+
if (root.name.length > BUY_CAPTURE_MAX_NAME_LENGTH) {
|
|
58631
|
+
throw new BuyCaptureError(
|
|
58632
|
+
"BUY_CAPTURE_BOUNDS_EXCEEDED",
|
|
58633
|
+
`${path}.name exceeds the ${BUY_CAPTURE_MAX_NAME_LENGTH}-character bound.`,
|
|
58634
|
+
{
|
|
58635
|
+
recovery: "Use the exact ERP document name.",
|
|
58636
|
+
context: {
|
|
58637
|
+
limit: BUY_CAPTURE_MAX_NAME_LENGTH,
|
|
58638
|
+
actual: root.name.length
|
|
58639
|
+
}
|
|
58640
|
+
}
|
|
58641
|
+
);
|
|
58642
|
+
}
|
|
58643
|
+
const id = `${root.doctype}:${root.name}`;
|
|
58644
|
+
if (seen.has(id)) {
|
|
58645
|
+
throw new BuyCaptureError(
|
|
58646
|
+
"BUY_CAPTURE_INVALID_INPUT",
|
|
58647
|
+
`${path} duplicates ${id}.`,
|
|
58648
|
+
{ recovery: "Each exact document reference may appear once." }
|
|
58649
|
+
);
|
|
58650
|
+
}
|
|
58651
|
+
seen.add(id);
|
|
58652
|
+
const expectedModified = root.expectedModified === void 0 ? void 0 : parseExpectedModified(root.expectedModified, `${path}.expectedModified`);
|
|
58653
|
+
return {
|
|
58654
|
+
doctype: root.doctype,
|
|
58655
|
+
name: root.name,
|
|
58656
|
+
...expectedModified !== void 0 ? { expectedModified } : {}
|
|
58657
|
+
};
|
|
58658
|
+
}
|
|
58659
|
+
function parseExpectedModified(value, path) {
|
|
58660
|
+
if (typeof value !== "string" || value.trim() === "" || value.trim() !== value) {
|
|
58661
|
+
throw new BuyCaptureError(
|
|
58662
|
+
"BUY_CAPTURE_INVALID_INPUT",
|
|
58663
|
+
`${path} must be the exact Frappe modified timestamp.`,
|
|
58664
|
+
{ recovery: "Pass the document's modified value, not a caller digest." }
|
|
58665
|
+
);
|
|
58666
|
+
}
|
|
58667
|
+
try {
|
|
58668
|
+
frappeDatetime(value, path);
|
|
58669
|
+
} catch {
|
|
58670
|
+
throw new BuyCaptureError(
|
|
58671
|
+
"BUY_CAPTURE_INVALID_INPUT",
|
|
58672
|
+
`${path} is not a Frappe datetime.`,
|
|
58673
|
+
{ recovery: "Pass the document's modified value." }
|
|
58674
|
+
);
|
|
58675
|
+
}
|
|
58676
|
+
return value;
|
|
58677
|
+
}
|
|
58678
|
+
async function parseCapturedDocument(value, index) {
|
|
58679
|
+
const path = `buy source capture.documents[${index}]`;
|
|
58680
|
+
const root = record2(value, path);
|
|
58681
|
+
const keys = Object.keys(root).toSorted();
|
|
58682
|
+
const optional2 = /* @__PURE__ */ new Set(["docstatus", "status", "children"]);
|
|
58683
|
+
const required2 = [
|
|
58684
|
+
"doctype",
|
|
58685
|
+
"fields",
|
|
58686
|
+
"fingerprint",
|
|
58687
|
+
"modified",
|
|
58688
|
+
"name",
|
|
58689
|
+
"sourceCategory"
|
|
58690
|
+
];
|
|
58691
|
+
const actualRequired = keys.filter((key) => !optional2.has(key)).toSorted();
|
|
58692
|
+
if (actualRequired.length !== required2.length || actualRequired.some((key, keyIndex) => key !== required2[keyIndex])) {
|
|
58693
|
+
throw new TypeError(`${path} contains missing or unsupported fields.`);
|
|
58694
|
+
}
|
|
58695
|
+
const extra = keys.filter(
|
|
58696
|
+
(key) => !required2.includes(key) && !optional2.has(key)
|
|
58697
|
+
);
|
|
58698
|
+
if (extra.length > 0) {
|
|
58699
|
+
throw new TypeError(`${path} contains missing or unsupported fields.`);
|
|
58700
|
+
}
|
|
58701
|
+
const doctype = oneOf(
|
|
58702
|
+
root.doctype,
|
|
58703
|
+
BUY_CAPTURE_DOCTYPE,
|
|
58704
|
+
`${path}.doctype`
|
|
58705
|
+
);
|
|
58706
|
+
const document = {
|
|
58707
|
+
doctype,
|
|
58708
|
+
name: nonEmpty(root.name, `${path}.name`),
|
|
58709
|
+
modified: frappeDatetime(root.modified, `${path}.modified`),
|
|
58710
|
+
...root.docstatus === void 0 ? {} : {
|
|
58711
|
+
docstatus: parseDocstatus(root.docstatus, `${path}.docstatus`)
|
|
58712
|
+
},
|
|
58713
|
+
...root.status === void 0 ? {} : { status: nonEmpty(root.status, `${path}.status`) },
|
|
58714
|
+
sourceCategory: nonEmpty(
|
|
58715
|
+
root.sourceCategory,
|
|
58716
|
+
`${path}.sourceCategory`
|
|
58717
|
+
),
|
|
58718
|
+
fields: parseClosedWireFields(
|
|
58719
|
+
root.fields,
|
|
58720
|
+
BUY_DOCTYPE_FIELD_SPECS[doctype],
|
|
58721
|
+
`${path}.fields`
|
|
58722
|
+
),
|
|
58723
|
+
...root.children === void 0 ? {} : {
|
|
58724
|
+
children: parseClosedWireChildren(
|
|
58725
|
+
doctype,
|
|
58726
|
+
root.children,
|
|
58727
|
+
`${path}.children`
|
|
58728
|
+
)
|
|
58729
|
+
},
|
|
58730
|
+
fingerprint: fingerprint(root.fingerprint, `${path}.fingerprint`)
|
|
58731
|
+
};
|
|
58732
|
+
if (document.sourceCategory !== BUY_DOCTYPE_SOURCE_CATEGORY[doctype]) {
|
|
58733
|
+
throw new TypeError(
|
|
58734
|
+
`${path}.sourceCategory must match the closed DocType; a generic document cannot masquerade as a price.`
|
|
58735
|
+
);
|
|
58736
|
+
}
|
|
58737
|
+
const { fingerprint: _ignored, ...identity } = document;
|
|
58738
|
+
const expectedFingerprint = await sha256FingerprintOfUtf8(
|
|
58739
|
+
canonicalJson2(identity, `${path} identity`)
|
|
58740
|
+
);
|
|
58741
|
+
if (document.fingerprint !== expectedFingerprint) {
|
|
58742
|
+
throw new TypeError(
|
|
58743
|
+
`${path}.fingerprint must be SHA-256 of the document projection without fingerprint.`
|
|
58744
|
+
);
|
|
58745
|
+
}
|
|
58746
|
+
return document;
|
|
58747
|
+
}
|
|
58748
|
+
function parseDocstatus(value, path) {
|
|
58749
|
+
if (value !== 0 && value !== 1 && value !== 2) {
|
|
58750
|
+
throw new TypeError(`${path} must be 0, 1, or 2.`);
|
|
58751
|
+
}
|
|
58752
|
+
return value;
|
|
58753
|
+
}
|
|
58754
|
+
function asRecord(value, name) {
|
|
58755
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
58756
|
+
throw new BuyCaptureError(
|
|
58757
|
+
"BUY_CAPTURE_INVALID_INPUT",
|
|
58758
|
+
`${name} must be an object.`,
|
|
58759
|
+
{ recovery: "Pass a JSON object with exact keys." }
|
|
58760
|
+
);
|
|
58761
|
+
}
|
|
58762
|
+
return value;
|
|
58763
|
+
}
|
|
58764
|
+
|
|
58765
|
+
// src/tools/buy.ts
|
|
58766
|
+
var buyTools = [
|
|
58767
|
+
{
|
|
58768
|
+
name: "erpnext_buy_capture",
|
|
58769
|
+
annotations: { readOnlyHint: true },
|
|
58770
|
+
description: "Read-only bounded capture of exact ERPNext commercial documents (Item, BOM, Item Price, Supplier Quotation, Supplier, Price List, UOM, Currency Exchange). Two skipCache reads must agree on modified and the closed projection fingerprint. Returns canonical JSON + SHA-256; does not store CAS, create BOM/RFQ/PO, compute a cost total, or qualify a live ERP. Not a registered purchase capability.",
|
|
58771
|
+
category: "buy",
|
|
58772
|
+
inputSchema: {
|
|
58773
|
+
type: "object",
|
|
58774
|
+
additionalProperties: false,
|
|
58775
|
+
required: ["documents"],
|
|
58776
|
+
properties: {
|
|
58777
|
+
documents: {
|
|
58778
|
+
type: "array",
|
|
58779
|
+
minItems: 1,
|
|
58780
|
+
maxItems: BUY_CAPTURE_MAX_DOCUMENTS,
|
|
58781
|
+
description: "Exact document references. Do not pass sourceInstance, URL, credentials, capturedAt, or a caller digest.",
|
|
58782
|
+
items: {
|
|
58783
|
+
type: "object",
|
|
58784
|
+
additionalProperties: false,
|
|
58785
|
+
required: ["doctype", "name"],
|
|
58786
|
+
properties: {
|
|
58787
|
+
doctype: {
|
|
58788
|
+
type: "string",
|
|
58789
|
+
enum: [...BUY_CAPTURE_DOCTYPE],
|
|
58790
|
+
description: "Closed Buy capture DocType"
|
|
58791
|
+
},
|
|
58792
|
+
name: {
|
|
58793
|
+
type: "string",
|
|
58794
|
+
minLength: 1,
|
|
58795
|
+
maxLength: 140,
|
|
58796
|
+
description: "Exact ERP document name"
|
|
58797
|
+
},
|
|
58798
|
+
expectedModified: {
|
|
58799
|
+
type: "string",
|
|
58800
|
+
description: "Optional exact Frappe modified timestamp to match"
|
|
58801
|
+
}
|
|
58802
|
+
}
|
|
58803
|
+
}
|
|
58804
|
+
}
|
|
58805
|
+
}
|
|
58806
|
+
},
|
|
58807
|
+
handler: async (input, ctx) => {
|
|
58808
|
+
return await runBuyCapture(input, { client: ctx.client });
|
|
58809
|
+
}
|
|
58810
|
+
}
|
|
58811
|
+
];
|
|
58812
|
+
|
|
57440
58813
|
// src/tools/mod.ts
|
|
57441
58814
|
var toolsByCategory = {
|
|
57442
58815
|
sales: salesTools,
|
|
@@ -57452,7 +58825,8 @@ var toolsByCategory = {
|
|
|
57452
58825
|
operations: operationsTools,
|
|
57453
58826
|
setup: setupTools,
|
|
57454
58827
|
analytics: analyticsTools,
|
|
57455
|
-
kanban: kanbanTools
|
|
58828
|
+
kanban: kanbanTools,
|
|
58829
|
+
buy: buyTools
|
|
57456
58830
|
};
|
|
57457
58831
|
var allTools = [
|
|
57458
58832
|
...salesTools,
|
|
@@ -57468,7 +58842,8 @@ var allTools = [
|
|
|
57468
58842
|
...operationsTools,
|
|
57469
58843
|
...setupTools,
|
|
57470
58844
|
...analyticsTools,
|
|
57471
|
-
...kanbanTools
|
|
58845
|
+
...kanbanTools,
|
|
58846
|
+
...buyTools
|
|
57472
58847
|
];
|
|
57473
58848
|
function getToolsByCategory(category) {
|
|
57474
58849
|
return toolsByCategory[category] ?? [];
|
|
@@ -58846,7 +60221,8 @@ var UI_VIEWERS = [
|
|
|
58846
60221
|
"chart-viewer",
|
|
58847
60222
|
"kpi-viewer",
|
|
58848
60223
|
"funnel-viewer",
|
|
58849
|
-
"kanban-viewer"
|
|
60224
|
+
"kanban-viewer",
|
|
60225
|
+
"buy-evidence-viewer"
|
|
58850
60226
|
];
|
|
58851
60227
|
var VIEWER_RESOURCE_META = {
|
|
58852
60228
|
ui: {
|
|
@@ -58860,6 +60236,110 @@ function withViewerResourceMeta(resource) {
|
|
|
58860
60236
|
return { ...resource, _meta: VIEWER_RESOURCE_META };
|
|
58861
60237
|
}
|
|
58862
60238
|
|
|
60239
|
+
// src/buy/manifest.ts
|
|
60240
|
+
var VIEW_APP_MANIFEST_SCHEMA = "io.casys.mcp.view-app-manifest/1.0";
|
|
60241
|
+
var VIEWER_SESSION_APPLY_ACTION = "viewer.session.apply";
|
|
60242
|
+
var BUY_VIEW_APP_MANIFEST = {
|
|
60243
|
+
schemaVersion: VIEW_APP_MANIFEST_SCHEMA,
|
|
60244
|
+
app: {
|
|
60245
|
+
id: BUY_VIEW_APP_ID,
|
|
60246
|
+
title: BUY_VIEW_APP_TITLE,
|
|
60247
|
+
version: BUY_VIEW_APP_VERSION
|
|
60248
|
+
},
|
|
60249
|
+
resources: [{
|
|
60250
|
+
uri: BUY_EVIDENCE_VIEWER_URI,
|
|
60251
|
+
ownership: "whole-view",
|
|
60252
|
+
resultSchemas: [BUY_RECORDED_RESULT_SCHEMA],
|
|
60253
|
+
acceptedActions: [VIEWER_SESSION_APPLY_ACTION],
|
|
60254
|
+
sessionSchemas: [BUY_RECORDED_SESSION_SCHEMA]
|
|
60255
|
+
}]
|
|
60256
|
+
};
|
|
60257
|
+
var BUY_VIEW_APP_MANIFEST_JSON = `${JSON.stringify(BUY_VIEW_APP_MANIFEST)}
|
|
60258
|
+
`;
|
|
60259
|
+
function parseBuyViewAppManifest(value) {
|
|
60260
|
+
const root = exactRecord(
|
|
60261
|
+
value,
|
|
60262
|
+
["schemaVersion", "app", "resources"],
|
|
60263
|
+
"Buy View App manifest"
|
|
60264
|
+
);
|
|
60265
|
+
literal2(
|
|
60266
|
+
root.schemaVersion,
|
|
60267
|
+
VIEW_APP_MANIFEST_SCHEMA,
|
|
60268
|
+
"Buy View App manifest.schemaVersion"
|
|
60269
|
+
);
|
|
60270
|
+
const app = exactRecord(
|
|
60271
|
+
root.app,
|
|
60272
|
+
["id", "title", "version"],
|
|
60273
|
+
"Buy View App manifest.app"
|
|
60274
|
+
);
|
|
60275
|
+
literal2(app.id, BUY_VIEW_APP_ID, "Buy View App manifest.app.id");
|
|
60276
|
+
literal2(app.title, BUY_VIEW_APP_TITLE, "Buy View App manifest.app.title");
|
|
60277
|
+
literal2(
|
|
60278
|
+
app.version,
|
|
60279
|
+
BUY_VIEW_APP_VERSION,
|
|
60280
|
+
"Buy View App manifest.app.version"
|
|
60281
|
+
);
|
|
60282
|
+
const resources = denseArray(
|
|
60283
|
+
root.resources,
|
|
60284
|
+
"Buy View App manifest.resources"
|
|
60285
|
+
);
|
|
60286
|
+
if (resources.length !== 1) {
|
|
60287
|
+
throw new TypeError(
|
|
60288
|
+
"Buy View App manifest.resources must contain exactly one whole view."
|
|
60289
|
+
);
|
|
60290
|
+
}
|
|
60291
|
+
const resource = exactRecord(
|
|
60292
|
+
resources[0],
|
|
60293
|
+
[
|
|
60294
|
+
"uri",
|
|
60295
|
+
"ownership",
|
|
60296
|
+
"resultSchemas",
|
|
60297
|
+
"acceptedActions",
|
|
60298
|
+
"sessionSchemas"
|
|
60299
|
+
],
|
|
60300
|
+
"Buy View App manifest.resources[0]"
|
|
60301
|
+
);
|
|
60302
|
+
literal2(
|
|
60303
|
+
resource.uri,
|
|
60304
|
+
BUY_EVIDENCE_VIEWER_URI,
|
|
60305
|
+
"Buy View App manifest.resources[0].uri"
|
|
60306
|
+
);
|
|
60307
|
+
literal2(
|
|
60308
|
+
resource.ownership,
|
|
60309
|
+
"whole-view",
|
|
60310
|
+
"Buy View App manifest.resources[0].ownership"
|
|
60311
|
+
);
|
|
60312
|
+
const resultSchemas = denseArray(
|
|
60313
|
+
resource.resultSchemas,
|
|
60314
|
+
"Buy View App manifest.resources[0].resultSchemas"
|
|
60315
|
+
);
|
|
60316
|
+
if (resultSchemas.length !== 1 || resultSchemas[0] !== BUY_RECORDED_RESULT_SCHEMA) {
|
|
60317
|
+
throw new TypeError(
|
|
60318
|
+
"Buy View App manifest result schemas must be io.casys.mcp-erpnext.buy-recorded-result/1.0 exactly."
|
|
60319
|
+
);
|
|
60320
|
+
}
|
|
60321
|
+
const acceptedActions = denseArray(
|
|
60322
|
+
resource.acceptedActions,
|
|
60323
|
+
"Buy View App manifest.resources[0].acceptedActions"
|
|
60324
|
+
);
|
|
60325
|
+
if (acceptedActions.length !== 1 || acceptedActions[0] !== VIEWER_SESSION_APPLY_ACTION) {
|
|
60326
|
+
throw new TypeError(
|
|
60327
|
+
"Buy View App manifest must accept viewer.session.apply exactly."
|
|
60328
|
+
);
|
|
60329
|
+
}
|
|
60330
|
+
const sessionSchemas = denseArray(
|
|
60331
|
+
resource.sessionSchemas,
|
|
60332
|
+
"Buy View App manifest.resources[0].sessionSchemas"
|
|
60333
|
+
);
|
|
60334
|
+
if (sessionSchemas.length !== 1 || sessionSchemas[0] !== BUY_RECORDED_SESSION_SCHEMA) {
|
|
60335
|
+
throw new TypeError(
|
|
60336
|
+
"Buy View App manifest must bind buy-recorded-session/1.0 exactly."
|
|
60337
|
+
);
|
|
60338
|
+
}
|
|
60339
|
+
return BUY_VIEW_APP_MANIFEST;
|
|
60340
|
+
}
|
|
60341
|
+
parseBuyViewAppManifest(BUY_VIEW_APP_MANIFEST);
|
|
60342
|
+
|
|
58863
60343
|
// src/ui/viewer-resource-paths.ts
|
|
58864
60344
|
function fileUrlToPath2(url2) {
|
|
58865
60345
|
const decodedPath = decodeURIComponent(url2.pathname);
|
|
@@ -59091,6 +60571,28 @@ function loadMrtrConfig() {
|
|
|
59091
60571
|
|
|
59092
60572
|
// server.ts
|
|
59093
60573
|
var DEFAULT_HTTP_PORT = 3012;
|
|
60574
|
+
function registerBuyViewAppManifest(server) {
|
|
60575
|
+
const bytes = new TextEncoder().encode(BUY_VIEW_APP_MANIFEST_JSON);
|
|
60576
|
+
server.registerResource(
|
|
60577
|
+
{
|
|
60578
|
+
uri: BUY_VIEW_APP_MANIFEST_URI,
|
|
60579
|
+
name: "ERPNext Buy Evidence App manifest",
|
|
60580
|
+
description: `Exact ${BUY_VIEW_APP_MANIFEST.app.id}@${BUY_VIEW_APP_MANIFEST.app.version} whole-view and recorded-session contract.`,
|
|
60581
|
+
mimeType: "application/json",
|
|
60582
|
+
size: bytes.byteLength
|
|
60583
|
+
},
|
|
60584
|
+
(requested) => {
|
|
60585
|
+
if (requested.toString() !== BUY_VIEW_APP_MANIFEST_URI) {
|
|
60586
|
+
throw new Error("Requested URI does not match the Buy App manifest.");
|
|
60587
|
+
}
|
|
60588
|
+
return {
|
|
60589
|
+
uri: BUY_VIEW_APP_MANIFEST_URI,
|
|
60590
|
+
mimeType: "application/json",
|
|
60591
|
+
text: BUY_VIEW_APP_MANIFEST_JSON
|
|
60592
|
+
};
|
|
60593
|
+
}
|
|
60594
|
+
);
|
|
60595
|
+
}
|
|
59094
60596
|
async function main() {
|
|
59095
60597
|
const args = getArgs3();
|
|
59096
60598
|
if (args.includes("--inspect")) {
|
|
@@ -59120,7 +60622,7 @@ async function main() {
|
|
|
59120
60622
|
);
|
|
59121
60623
|
const server = new McpApp({
|
|
59122
60624
|
name: "mcp-erpnext",
|
|
59123
|
-
version: "3.1.0-beta.
|
|
60625
|
+
version: "3.1.0-beta.9",
|
|
59124
60626
|
transport: "stateless",
|
|
59125
60627
|
cache: {
|
|
59126
60628
|
ttlMs: 36e5,
|
|
@@ -59141,6 +60643,7 @@ async function main() {
|
|
|
59141
60643
|
const mcpTools = toolsClient.toMCPFormat();
|
|
59142
60644
|
const handlers = toolsClient.buildHandlersMap();
|
|
59143
60645
|
server.registerTools(mcpTools, handlers);
|
|
60646
|
+
registerBuyViewAppManifest(server);
|
|
59144
60647
|
for (const viewerName of UI_VIEWERS) {
|
|
59145
60648
|
const distPath = resolveViewerDistPath2(
|
|
59146
60649
|
import.meta.url,
|