@milaboratories/pl-errors 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +148 -0
- package/dist/index.mjs.map +1 -0
- package/dist/parsed_error.d.ts +86 -0
- package/dist/parsed_error.d.ts.map +1 -0
- package/package.json +36 -0
- package/src/index.ts +1 -0
- package/src/parsed_error.test.ts +84 -0
- package/src/parsed_error.ts +276 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";var $=Object.defineProperty;var y=(e,r,t)=>r in e?$(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t;var c=(e,r,t)=>y(e,typeof r!="symbol"?r+"":r,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("zod"),g=require("@milaboratories/pl-client");class d extends Error{constructor(t,o,s,n,i,a,l){super(t);c(this,"message");this.fullMessage=t,this.plErrorType=o,this.plMessage=s,this.errors=n,this.fieldName=i,this.resource=a,this.resourceType=l,this.name="PlErrorReport",this.message=this.toString()}toString(){const t=this.resourceType?`${g.resourceTypeToString(this.resourceType)},`:"",o=this.resource?g.resourceIdToString(this.resource):"",s=this.fieldName?`/${this.fieldName}`:"",n=this.plErrorType?`error type: ${this.plErrorType}`:"",i=this.errors.map(a=>a.message).join(`
|
|
2
|
+
|
|
3
|
+
`);return`PlErrorReport: resource: ${t} ${o}${s}
|
|
4
|
+
${n}
|
|
5
|
+
${i}`}}class E extends Error{constructor(r){super(r),this.message=r,this.name="PlInternalError"}}class f extends Error{constructor(t,o,s,n){super(t);c(this,"message");this.fullMessage=t,this.templateName=o,this.tengoMessage=s,this.tengoStacktrace=n,this.name="PlWorkflowError",this.message=this.toString()}toString(){return`PlWorkflowError:
|
|
6
|
+
template: ${this.templateName}
|
|
7
|
+
message:
|
|
8
|
+
${this.tengoMessage}
|
|
9
|
+
tengo stacktrace:
|
|
10
|
+
${this.tengoStacktrace}
|
|
11
|
+
|
|
12
|
+
full message:
|
|
13
|
+
${this.fullMessage}`}}class h extends Error{constructor(t,o,s,n,i){super(t);c(this,"message");this.fullMessage=t,this.commandName=o,this.exitCode=s,this.stdout=n,this.workingDirectory=i,this.name="PlRunnerError",this.message=this.toString()}toString(){return`PlRunnerError:
|
|
14
|
+
command: ${this.commandName}
|
|
15
|
+
exit code: ${this.exitCode}
|
|
16
|
+
working directory: ${this.workingDirectory}
|
|
17
|
+
stdout:
|
|
18
|
+
${this.stdout}
|
|
19
|
+
|
|
20
|
+
full message:
|
|
21
|
+
${this.fullMessage}`}}class S extends h{constructor(t,o,s,n,i){super(t,o,s,n,i);c(this,"message");this.name="PlMonetizationError",this.message=this.toString()}toString(){return`PlMonetizationError:
|
|
22
|
+
command: ${this.commandName}
|
|
23
|
+
exit code: ${this.exitCode}
|
|
24
|
+
working directory: ${this.workingDirectory}
|
|
25
|
+
stdout:
|
|
26
|
+
${this.stdout}
|
|
27
|
+
|
|
28
|
+
full message:
|
|
29
|
+
${this.fullMessage}`}}const w=u.z.object({errorType:u.z.string(),message:u.z.string()});function T(e,r,t,o){const s=w.parse(JSON.parse(e)),n=P(s.message);return new d(e,s.errorType,s.message,n,o,r,t)}function P(e){const r={stage:"initial",value:[],result:[]};for(const o of e.split(`
|
|
30
|
+
`)){if(r.stage=="initial")r.stage="path";else if(r.stage=="path"&&!m(o))r.stage="message";else if(r.stage=="message"&&m(o)){r.stage="path";const s=r.value.join(`
|
|
31
|
+
`);r.result.push(p(s)),r.value=[]}r.value.push(o)}const t=r.value.join(`
|
|
32
|
+
`);return r.result.push(p(t)),r.result}function m(e){for(const r of["U","I","O","S","OTW","D","MTW"])if(e.startsWith(`[${r}]`))return!0;return!1}function p(e){const r=/working directory: "(.*)"[\s\S]failed to run command: "([^"]+)" exited with code (\d+)\.[\s\S]*?Here is the latest command output:[\s\S]*?\t([\s\S]*)/,t=e.match(r);if(t){const n=t[1],i=t[2],a=parseInt(t[3],10),l=t[4].trim();return i.endsWith("mnz-client")&&a==1?new S(e,i,a,l,n):new h(e,i,a,l,n)}const o=/cannot eval code: cannot eval template: template: (.+)\n\t(.*?)\n\t(at [\s\S]*)/,s=e.match(o);if(s){const n=s[1],i=s[2],a=s[3];return new f(e,n,i,a)}return new E(e)}exports.PlErrorReport=d;exports.PlInternalError=E;exports.PlMonetizationError=S;exports.PlRunnerError=h;exports.PlTengoError=f;exports.parsePlError=T;exports.parseSubErrors=P;
|
|
33
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/parsed_error.ts"],"sourcesContent":["/** Pl Backend throws arbitrary errors, and we're trying to parse them here. */\n\nimport { z } from \"zod\";\nimport { ResourceId, resourceIdToString, ResourceType, resourceTypeToString } from \"@milaboratories/pl-client\";\n\n/**\n * A parsed error from the Pl backend.\n * It contains several suberrors, which could be one or different causes of the error.\n */\nexport class PlErrorReport extends Error {\n message: string;\n\n constructor(\n /** Full message from the Pl backend. */\n public readonly fullMessage: string,\n\n /** Either CID conflict or a error from controller. */\n public readonly plErrorType: string,\n\n /** Parsed pl backend message that will be futher parsed into suberrors. */\n public readonly plMessage: string,\n\n /** Could be several different errors, the name is from AggregateError. */\n public readonly errors: PlCoreError[],\n\n /** Optional info about a resource where the error happened. */\n public readonly fieldName?: string,\n public readonly resource?: ResourceId,\n public readonly resourceType?: ResourceType,\n ) {\n super(fullMessage);\n this.name = 'PlErrorReport';\n this.message = this.toString();\n }\n\n toString() {\n const rt = this.resourceType ? `${resourceTypeToString(this.resourceType)},` : '';\n const r = this.resource ? resourceIdToString(this.resource) : '';\n const f = this.fieldName ? `/${this.fieldName}` : '';\n const errType = this.plErrorType ? `error type: ${this.plErrorType}` : '';\n const subErrors = this.errors.map(e => e.message).join('\\n\\n');\n\n return `PlErrorReport: resource: ${rt} ${r}${f}\n${errType}\n${subErrors}`;\n }\n}\n\n/**\n * A suberror of a parsed error.\n */\nexport type PlCoreError =\n | PlInternalError\n | PlTengoError\n | PlRunnerError\n | PlMonetizationError;\n\n/**\n * An general error when we couldn't parse the cause.\n */\nexport class PlInternalError extends Error {\n constructor(\n public readonly message: string,\n ) {\n super(message);\n this.name = 'PlInternalError';\n }\n}\n\n/**\n * Happens when workflow template panics.\n */\nexport class PlTengoError extends Error {\n message: string;\n\n constructor(\n public readonly fullMessage: string,\n public readonly templateName: string,\n public readonly tengoMessage: string,\n public readonly tengoStacktrace: string,\n ) {\n super(fullMessage);\n this.name = 'PlWorkflowError';\n this.message = this.toString();\n }\n\n toString() {\n return `PlWorkflowError:\ntemplate: ${this.templateName}\nmessage:\n${this.tengoMessage}\ntengo stacktrace:\n${this.tengoStacktrace}\n\nfull message:\n${this.fullMessage}`;\n }\n}\n\n/**\n * Happens when a command fails to run.\n */\nexport class PlRunnerError extends Error {\n message: string;\n\n constructor(\n public readonly fullMessage: string,\n public readonly commandName: string,\n public readonly exitCode: number,\n public readonly stdout: string,\n public readonly workingDirectory: string,\n ) {\n super(fullMessage);\n this.name = 'PlRunnerError';\n this.message = this.toString();\n }\n\n toString() {\n return `PlRunnerError:\ncommand: ${this.commandName}\nexit code: ${this.exitCode}\nworking directory: ${this.workingDirectory}\nstdout:\n${this.stdout}\n\nfull message:\n${this.fullMessage}`;\n }\n}\n\n/**\n * Happens when a monetization command fails to run.\n */\nexport class PlMonetizationError extends PlRunnerError {\n message: string;\n\n constructor(\n fullMessage: string,\n commandName: string,\n exitCode: number,\n stdout: string,\n workingDirectory: string,\n ) {\n super(fullMessage, commandName, exitCode, stdout, workingDirectory);\n this.name = 'PlMonetizationError';\n this.message = this.toString();\n }\n\n toString() {\n return `PlMonetizationError:\ncommand: ${this.commandName}\nexit code: ${this.exitCode}\nworking directory: ${this.workingDirectory}\nstdout:\n${this.stdout}\n\nfull message:\n${this.fullMessage}`;\n }\n}\n\n/**\n * How the Pl backend represents an error.\n */\nconst backendErrorSchema = z.object({\n errorType: z.string(),\n message: z.string(),\n});\n\n/**\n * Parses a Pl error and suberrors from the Pl backend.\n */\nexport function parsePlError(\n error: string,\n\n resource?: ResourceId,\n resourceType?: ResourceType,\n field?: string,\n): PlErrorReport {\n const parsed = backendErrorSchema.parse(JSON.parse(error));\n const subErrors = parseSubErrors(parsed.message);\n\n return new PlErrorReport(\n error,\n parsed.errorType,\n parsed.message,\n subErrors,\n\n field,\n resource,\n resourceType,\n );\n}\n\n/**\n * Reduces over the lines of the pl error message\n * to extract messages, and categorizes them.\n */\nexport function parseSubErrors(message: string): PlCoreError[] {\n // the state of this reducing function\n const state = {\n stage: 'initial' as 'initial' | 'path' | 'message',\n value: [] as string[],\n result: [] as PlCoreError[],\n };\n\n for (const line of message.split('\\n')) {\n if (state.stage == 'initial') {\n // we need initial stage because apparently the first line\n // of the error doesn't have [I], but is a path line.\n state.stage = 'path';\n\n } else if (state.stage == 'path' && !isPath(line)) {\n state.stage = 'message';\n\n } else if (state.stage == 'message' && isPath(line)) {\n state.stage = 'path';\n const text = state.value.join('\\n');\n state.result.push(parseCoreError(text));\n state.value = [];\n }\n\n state.value.push(line);\n }\n\n const text = state.value.join('\\n');\n state.result.push(parseCoreError(text));\n\n return state.result;\n}\n\nfunction isPath(line: string): boolean {\n for (const fieldType of ['U', 'I', 'O', 'S', 'OTW', 'D', 'MTW']) {\n if (line.startsWith(`[${fieldType}]`))\n return true;\n }\n\n return false;\n}\n\n/**\n * Parses a suberror from the Pl backend.\n */\nfunction parseCoreError(message: string): PlCoreError {\n // trying to parse a runner or monetization error.\n // https://regex101.com/r/tmKLj7/1\n const runnerErrorRegex = /working directory: \"(.*)\"[\\s\\S]failed to run command: \"([^\"]+)\" exited with code (\\d+)\\.[\\s\\S]*?Here is the latest command output:[\\s\\S]*?\\t([\\s\\S]*)/;\n const match = message.match(runnerErrorRegex);\n if (match) {\n const workingDirectory = match[1];\n const command = match[2];\n const exitCode = parseInt(match[3], 10);\n const stdout = match[4].trim();\n\n if (command.endsWith(`mnz-client`) && exitCode == 1) {\n return new PlMonetizationError(message, command, exitCode, stdout, workingDirectory);\n }\n\n return new PlRunnerError(message, command, exitCode, stdout, workingDirectory);\n }\n\n // trying to parse a Tengo error.\n // https://regex101.com/r/1a7RpO/1\n const workflowErrorRegex = /cannot eval code: cannot eval template: template: (.+)\\n\\t(.*?)\\n\\t(at [\\s\\S]*)/;\n const workflowMatch = message.match(workflowErrorRegex);\n if (workflowMatch) {\n const templateName = workflowMatch[1];\n const errorMessage = workflowMatch[2];\n const stackTrace = workflowMatch[3];\n\n return new PlTengoError(message, templateName, errorMessage, stackTrace);\n }\n\n // if we couldn't parse the error, return a general error.\n return new PlInternalError(message);\n}\n"],"names":["PlErrorReport","fullMessage","plErrorType","plMessage","errors","fieldName","resource","resourceType","__publicField","rt","resourceTypeToString","r","resourceIdToString","f","errType","subErrors","e","PlInternalError","message","PlTengoError","templateName","tengoMessage","tengoStacktrace","PlRunnerError","commandName","exitCode","stdout","workingDirectory","PlMonetizationError","backendErrorSchema","z","parsePlError","error","field","parsed","parseSubErrors","state","line","isPath","text","parseCoreError","fieldType","runnerErrorRegex","match","command","workflowErrorRegex","workflowMatch","errorMessage","stackTrace"],"mappings":"kTASO,MAAMA,UAAsB,KAAM,CAGvC,YAEkBC,EAGAC,EAGAC,EAGAC,EAGAC,EACAC,EACAC,EAChB,CACA,MAAMN,CAAW,EApBnBO,EAAA,gBAIkB,KAAA,YAAAP,EAGA,KAAA,YAAAC,EAGA,KAAA,UAAAC,EAGA,KAAA,OAAAC,EAGA,KAAA,UAAAC,EACA,KAAA,SAAAC,EACA,KAAA,aAAAC,EAGhB,KAAK,KAAO,gBACP,KAAA,QAAU,KAAK,SAAS,CAAA,CAG/B,UAAW,CACH,MAAAE,EAAK,KAAK,aAAe,GAAGC,uBAAqB,KAAK,YAAY,CAAC,IAAM,GACzEC,EAAI,KAAK,SAAWC,EAAmB,mBAAA,KAAK,QAAQ,EAAI,GACxDC,EAAI,KAAK,UAAY,IAAI,KAAK,SAAS,GAAK,GAC5CC,EAAU,KAAK,YAAc,eAAe,KAAK,WAAW,GAAK,GACjEC,EAAY,KAAK,OAAO,OAASC,EAAE,OAAO,EAAE,KAAK;AAAA;AAAA,CAAM,EAE7D,MAAO,4BAA4BP,CAAE,IAAIE,CAAC,GAAGE,CAAC;AAAA,EAChDC,CAAO;AAAA,EACPC,CAAS,EAAA,CAEX,CAcO,MAAME,UAAwB,KAAM,CACzC,YACkBC,EAChB,CACA,MAAMA,CAAO,EAFG,KAAA,QAAAA,EAGhB,KAAK,KAAO,iBAAA,CAEhB,CAKO,MAAMC,UAAqB,KAAM,CAGtC,YACkBlB,EACAmB,EACAC,EACAC,EAChB,CACA,MAAMrB,CAAW,EARnBO,EAAA,gBAGkB,KAAA,YAAAP,EACA,KAAA,aAAAmB,EACA,KAAA,aAAAC,EACA,KAAA,gBAAAC,EAGhB,KAAK,KAAO,kBACP,KAAA,QAAU,KAAK,SAAS,CAAA,CAG/B,UAAW,CACF,MAAA;AAAA,YACC,KAAK,YAAY;AAAA;AAAA,EAE3B,KAAK,YAAY;AAAA;AAAA,EAEjB,KAAK,eAAe;AAAA;AAAA;AAAA,EAGpB,KAAK,WAAW,EAAA,CAElB,CAKO,MAAMC,UAAsB,KAAM,CAGvC,YACkBtB,EACAuB,EACAC,EACAC,EACAC,EAChB,CACA,MAAM1B,CAAW,EATnBO,EAAA,gBAGkB,KAAA,YAAAP,EACA,KAAA,YAAAuB,EACA,KAAA,SAAAC,EACA,KAAA,OAAAC,EACA,KAAA,iBAAAC,EAGhB,KAAK,KAAO,gBACP,KAAA,QAAU,KAAK,SAAS,CAAA,CAG/B,UAAW,CACF,MAAA;AAAA,WACA,KAAK,WAAW;AAAA,aACd,KAAK,QAAQ;AAAA,qBACL,KAAK,gBAAgB;AAAA;AAAA,EAExC,KAAK,MAAM;AAAA;AAAA;AAAA,EAGX,KAAK,WAAW,EAAA,CAElB,CAKO,MAAMC,UAA4BL,CAAc,CAGrD,YACEtB,EACAuB,EACAC,EACAC,EACAC,EACA,CACA,MAAM1B,EAAauB,EAAaC,EAAUC,EAAQC,CAAgB,EATpEnB,EAAA,gBAUE,KAAK,KAAO,sBACP,KAAA,QAAU,KAAK,SAAS,CAAA,CAG/B,UAAW,CACF,MAAA;AAAA,WACA,KAAK,WAAW;AAAA,aACd,KAAK,QAAQ;AAAA,qBACL,KAAK,gBAAgB;AAAA;AAAA,EAExC,KAAK,MAAM;AAAA;AAAA;AAAA,EAGX,KAAK,WAAW,EAAA,CAElB,CAKA,MAAMqB,EAAqBC,IAAE,OAAO,CAClC,UAAWA,IAAE,OAAO,EACpB,QAASA,IAAE,OAAO,CACpB,CAAC,EAKM,SAASC,EACdC,EAEA1B,EACAC,EACA0B,EACe,CACf,MAAMC,EAASL,EAAmB,MAAM,KAAK,MAAMG,CAAK,CAAC,EACnDjB,EAAYoB,EAAeD,EAAO,OAAO,EAE/C,OAAO,IAAIlC,EACTgC,EACAE,EAAO,UACPA,EAAO,QACPnB,EAEAkB,EACA3B,EACAC,CACF,CACF,CAMO,SAAS4B,EAAejB,EAAgC,CAE7D,MAAMkB,EAAQ,CACZ,MAAO,UACP,MAAO,CAAC,EACR,OAAQ,CAAA,CACV,EAEA,UAAWC,KAAQnB,EAAQ,MAAM;AAAA,CAAI,EAAG,CAClC,GAAAkB,EAAM,OAAS,UAGjBA,EAAM,MAAQ,eAELA,EAAM,OAAS,QAAU,CAACE,EAAOD,CAAI,EAC9CD,EAAM,MAAQ,kBAELA,EAAM,OAAS,WAAaE,EAAOD,CAAI,EAAG,CACnDD,EAAM,MAAQ,OACd,MAAMG,EAAOH,EAAM,MAAM,KAAK;AAAA,CAAI,EAClCA,EAAM,OAAO,KAAKI,EAAeD,CAAI,CAAC,EACtCH,EAAM,MAAQ,CAAC,CAAA,CAGXA,EAAA,MAAM,KAAKC,CAAI,CAAA,CAGvB,MAAME,EAAOH,EAAM,MAAM,KAAK;AAAA,CAAI,EAClC,OAAAA,EAAM,OAAO,KAAKI,EAAeD,CAAI,CAAC,EAE/BH,EAAM,MACf,CAEA,SAASE,EAAOD,EAAuB,CAC1B,UAAAI,IAAa,CAAC,IAAK,IAAK,IAAK,IAAK,MAAO,IAAK,KAAK,EAC5D,GAAIJ,EAAK,WAAW,IAAII,CAAS,GAAG,EAC3B,MAAA,GAGJ,MAAA,EACT,CAKA,SAASD,EAAetB,EAA8B,CAGpD,MAAMwB,EAAmB,wJACnBC,EAAQzB,EAAQ,MAAMwB,CAAgB,EAC5C,GAAIC,EAAO,CACH,MAAAhB,EAAmBgB,EAAM,CAAC,EAC1BC,EAAUD,EAAM,CAAC,EACjBlB,EAAW,SAASkB,EAAM,CAAC,EAAG,EAAE,EAChCjB,EAASiB,EAAM,CAAC,EAAE,KAAK,EAE7B,OAAIC,EAAQ,SAAS,YAAY,GAAKnB,GAAY,EACzC,IAAIG,EAAoBV,EAAS0B,EAASnB,EAAUC,EAAQC,CAAgB,EAG9E,IAAIJ,EAAcL,EAAS0B,EAASnB,EAAUC,EAAQC,CAAgB,CAAA,CAK/E,MAAMkB,EAAqB,kFACrBC,EAAgB5B,EAAQ,MAAM2B,CAAkB,EACtD,GAAIC,EAAe,CACX,MAAA1B,EAAe0B,EAAc,CAAC,EAC9BC,EAAeD,EAAc,CAAC,EAC9BE,EAAaF,EAAc,CAAC,EAElC,OAAO,IAAI3B,EAAaD,EAASE,EAAc2B,EAAcC,CAAU,CAAA,CAIlE,OAAA,IAAI/B,EAAgBC,CAAO,CACpC"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
var p = Object.defineProperty;
|
|
2
|
+
var d = (e, t, r) => t in e ? p(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[t] = r;
|
|
3
|
+
var c = (e, t, r) => d(e, typeof t != "symbol" ? t + "" : t, r);
|
|
4
|
+
import { z as u } from "zod";
|
|
5
|
+
import { resourceTypeToString as f, resourceIdToString as E } from "@milaboratories/pl-client";
|
|
6
|
+
class $ extends Error {
|
|
7
|
+
constructor(r, o, s, n, i, a, l) {
|
|
8
|
+
super(r);
|
|
9
|
+
c(this, "message");
|
|
10
|
+
this.fullMessage = r, this.plErrorType = o, this.plMessage = s, this.errors = n, this.fieldName = i, this.resource = a, this.resourceType = l, this.name = "PlErrorReport", this.message = this.toString();
|
|
11
|
+
}
|
|
12
|
+
toString() {
|
|
13
|
+
const r = this.resourceType ? `${f(this.resourceType)},` : "", o = this.resource ? E(this.resource) : "", s = this.fieldName ? `/${this.fieldName}` : "", n = this.plErrorType ? `error type: ${this.plErrorType}` : "", i = this.errors.map((a) => a.message).join(`
|
|
14
|
+
|
|
15
|
+
`);
|
|
16
|
+
return `PlErrorReport: resource: ${r} ${o}${s}
|
|
17
|
+
${n}
|
|
18
|
+
${i}`;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
class S extends Error {
|
|
22
|
+
constructor(t) {
|
|
23
|
+
super(t), this.message = t, this.name = "PlInternalError";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
class w extends Error {
|
|
27
|
+
constructor(r, o, s, n) {
|
|
28
|
+
super(r);
|
|
29
|
+
c(this, "message");
|
|
30
|
+
this.fullMessage = r, this.templateName = o, this.tengoMessage = s, this.tengoStacktrace = n, this.name = "PlWorkflowError", this.message = this.toString();
|
|
31
|
+
}
|
|
32
|
+
toString() {
|
|
33
|
+
return `PlWorkflowError:
|
|
34
|
+
template: ${this.templateName}
|
|
35
|
+
message:
|
|
36
|
+
${this.tengoMessage}
|
|
37
|
+
tengo stacktrace:
|
|
38
|
+
${this.tengoStacktrace}
|
|
39
|
+
|
|
40
|
+
full message:
|
|
41
|
+
${this.fullMessage}`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
class g extends Error {
|
|
45
|
+
constructor(r, o, s, n, i) {
|
|
46
|
+
super(r);
|
|
47
|
+
c(this, "message");
|
|
48
|
+
this.fullMessage = r, this.commandName = o, this.exitCode = s, this.stdout = n, this.workingDirectory = i, this.name = "PlRunnerError", this.message = this.toString();
|
|
49
|
+
}
|
|
50
|
+
toString() {
|
|
51
|
+
return `PlRunnerError:
|
|
52
|
+
command: ${this.commandName}
|
|
53
|
+
exit code: ${this.exitCode}
|
|
54
|
+
working directory: ${this.workingDirectory}
|
|
55
|
+
stdout:
|
|
56
|
+
${this.stdout}
|
|
57
|
+
|
|
58
|
+
full message:
|
|
59
|
+
${this.fullMessage}`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
class y extends g {
|
|
63
|
+
constructor(r, o, s, n, i) {
|
|
64
|
+
super(r, o, s, n, i);
|
|
65
|
+
c(this, "message");
|
|
66
|
+
this.name = "PlMonetizationError", this.message = this.toString();
|
|
67
|
+
}
|
|
68
|
+
toString() {
|
|
69
|
+
return `PlMonetizationError:
|
|
70
|
+
command: ${this.commandName}
|
|
71
|
+
exit code: ${this.exitCode}
|
|
72
|
+
working directory: ${this.workingDirectory}
|
|
73
|
+
stdout:
|
|
74
|
+
${this.stdout}
|
|
75
|
+
|
|
76
|
+
full message:
|
|
77
|
+
${this.fullMessage}`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const x = u.object({
|
|
81
|
+
errorType: u.string(),
|
|
82
|
+
message: u.string()
|
|
83
|
+
});
|
|
84
|
+
function N(e, t, r, o) {
|
|
85
|
+
const s = x.parse(JSON.parse(e)), n = T(s.message);
|
|
86
|
+
return new $(
|
|
87
|
+
e,
|
|
88
|
+
s.errorType,
|
|
89
|
+
s.message,
|
|
90
|
+
n,
|
|
91
|
+
o,
|
|
92
|
+
t,
|
|
93
|
+
r
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
function T(e) {
|
|
97
|
+
const t = {
|
|
98
|
+
stage: "initial",
|
|
99
|
+
value: [],
|
|
100
|
+
result: []
|
|
101
|
+
};
|
|
102
|
+
for (const o of e.split(`
|
|
103
|
+
`)) {
|
|
104
|
+
if (t.stage == "initial")
|
|
105
|
+
t.stage = "path";
|
|
106
|
+
else if (t.stage == "path" && !h(o))
|
|
107
|
+
t.stage = "message";
|
|
108
|
+
else if (t.stage == "message" && h(o)) {
|
|
109
|
+
t.stage = "path";
|
|
110
|
+
const s = t.value.join(`
|
|
111
|
+
`);
|
|
112
|
+
t.result.push(m(s)), t.value = [];
|
|
113
|
+
}
|
|
114
|
+
t.value.push(o);
|
|
115
|
+
}
|
|
116
|
+
const r = t.value.join(`
|
|
117
|
+
`);
|
|
118
|
+
return t.result.push(m(r)), t.result;
|
|
119
|
+
}
|
|
120
|
+
function h(e) {
|
|
121
|
+
for (const t of ["U", "I", "O", "S", "OTW", "D", "MTW"])
|
|
122
|
+
if (e.startsWith(`[${t}]`))
|
|
123
|
+
return !0;
|
|
124
|
+
return !1;
|
|
125
|
+
}
|
|
126
|
+
function m(e) {
|
|
127
|
+
const t = /working directory: "(.*)"[\s\S]failed to run command: "([^"]+)" exited with code (\d+)\.[\s\S]*?Here is the latest command output:[\s\S]*?\t([\s\S]*)/, r = e.match(t);
|
|
128
|
+
if (r) {
|
|
129
|
+
const n = r[1], i = r[2], a = parseInt(r[3], 10), l = r[4].trim();
|
|
130
|
+
return i.endsWith("mnz-client") && a == 1 ? new y(e, i, a, l, n) : new g(e, i, a, l, n);
|
|
131
|
+
}
|
|
132
|
+
const o = /cannot eval code: cannot eval template: template: (.+)\n\t(.*?)\n\t(at [\s\S]*)/, s = e.match(o);
|
|
133
|
+
if (s) {
|
|
134
|
+
const n = s[1], i = s[2], a = s[3];
|
|
135
|
+
return new w(e, n, i, a);
|
|
136
|
+
}
|
|
137
|
+
return new S(e);
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
$ as PlErrorReport,
|
|
141
|
+
S as PlInternalError,
|
|
142
|
+
y as PlMonetizationError,
|
|
143
|
+
g as PlRunnerError,
|
|
144
|
+
w as PlTengoError,
|
|
145
|
+
N as parsePlError,
|
|
146
|
+
T as parseSubErrors
|
|
147
|
+
};
|
|
148
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/parsed_error.ts"],"sourcesContent":["/** Pl Backend throws arbitrary errors, and we're trying to parse them here. */\n\nimport { z } from \"zod\";\nimport { ResourceId, resourceIdToString, ResourceType, resourceTypeToString } from \"@milaboratories/pl-client\";\n\n/**\n * A parsed error from the Pl backend.\n * It contains several suberrors, which could be one or different causes of the error.\n */\nexport class PlErrorReport extends Error {\n message: string;\n\n constructor(\n /** Full message from the Pl backend. */\n public readonly fullMessage: string,\n\n /** Either CID conflict or a error from controller. */\n public readonly plErrorType: string,\n\n /** Parsed pl backend message that will be futher parsed into suberrors. */\n public readonly plMessage: string,\n\n /** Could be several different errors, the name is from AggregateError. */\n public readonly errors: PlCoreError[],\n\n /** Optional info about a resource where the error happened. */\n public readonly fieldName?: string,\n public readonly resource?: ResourceId,\n public readonly resourceType?: ResourceType,\n ) {\n super(fullMessage);\n this.name = 'PlErrorReport';\n this.message = this.toString();\n }\n\n toString() {\n const rt = this.resourceType ? `${resourceTypeToString(this.resourceType)},` : '';\n const r = this.resource ? resourceIdToString(this.resource) : '';\n const f = this.fieldName ? `/${this.fieldName}` : '';\n const errType = this.plErrorType ? `error type: ${this.plErrorType}` : '';\n const subErrors = this.errors.map(e => e.message).join('\\n\\n');\n\n return `PlErrorReport: resource: ${rt} ${r}${f}\n${errType}\n${subErrors}`;\n }\n}\n\n/**\n * A suberror of a parsed error.\n */\nexport type PlCoreError =\n | PlInternalError\n | PlTengoError\n | PlRunnerError\n | PlMonetizationError;\n\n/**\n * An general error when we couldn't parse the cause.\n */\nexport class PlInternalError extends Error {\n constructor(\n public readonly message: string,\n ) {\n super(message);\n this.name = 'PlInternalError';\n }\n}\n\n/**\n * Happens when workflow template panics.\n */\nexport class PlTengoError extends Error {\n message: string;\n\n constructor(\n public readonly fullMessage: string,\n public readonly templateName: string,\n public readonly tengoMessage: string,\n public readonly tengoStacktrace: string,\n ) {\n super(fullMessage);\n this.name = 'PlWorkflowError';\n this.message = this.toString();\n }\n\n toString() {\n return `PlWorkflowError:\ntemplate: ${this.templateName}\nmessage:\n${this.tengoMessage}\ntengo stacktrace:\n${this.tengoStacktrace}\n\nfull message:\n${this.fullMessage}`;\n }\n}\n\n/**\n * Happens when a command fails to run.\n */\nexport class PlRunnerError extends Error {\n message: string;\n\n constructor(\n public readonly fullMessage: string,\n public readonly commandName: string,\n public readonly exitCode: number,\n public readonly stdout: string,\n public readonly workingDirectory: string,\n ) {\n super(fullMessage);\n this.name = 'PlRunnerError';\n this.message = this.toString();\n }\n\n toString() {\n return `PlRunnerError:\ncommand: ${this.commandName}\nexit code: ${this.exitCode}\nworking directory: ${this.workingDirectory}\nstdout:\n${this.stdout}\n\nfull message:\n${this.fullMessage}`;\n }\n}\n\n/**\n * Happens when a monetization command fails to run.\n */\nexport class PlMonetizationError extends PlRunnerError {\n message: string;\n\n constructor(\n fullMessage: string,\n commandName: string,\n exitCode: number,\n stdout: string,\n workingDirectory: string,\n ) {\n super(fullMessage, commandName, exitCode, stdout, workingDirectory);\n this.name = 'PlMonetizationError';\n this.message = this.toString();\n }\n\n toString() {\n return `PlMonetizationError:\ncommand: ${this.commandName}\nexit code: ${this.exitCode}\nworking directory: ${this.workingDirectory}\nstdout:\n${this.stdout}\n\nfull message:\n${this.fullMessage}`;\n }\n}\n\n/**\n * How the Pl backend represents an error.\n */\nconst backendErrorSchema = z.object({\n errorType: z.string(),\n message: z.string(),\n});\n\n/**\n * Parses a Pl error and suberrors from the Pl backend.\n */\nexport function parsePlError(\n error: string,\n\n resource?: ResourceId,\n resourceType?: ResourceType,\n field?: string,\n): PlErrorReport {\n const parsed = backendErrorSchema.parse(JSON.parse(error));\n const subErrors = parseSubErrors(parsed.message);\n\n return new PlErrorReport(\n error,\n parsed.errorType,\n parsed.message,\n subErrors,\n\n field,\n resource,\n resourceType,\n );\n}\n\n/**\n * Reduces over the lines of the pl error message\n * to extract messages, and categorizes them.\n */\nexport function parseSubErrors(message: string): PlCoreError[] {\n // the state of this reducing function\n const state = {\n stage: 'initial' as 'initial' | 'path' | 'message',\n value: [] as string[],\n result: [] as PlCoreError[],\n };\n\n for (const line of message.split('\\n')) {\n if (state.stage == 'initial') {\n // we need initial stage because apparently the first line\n // of the error doesn't have [I], but is a path line.\n state.stage = 'path';\n\n } else if (state.stage == 'path' && !isPath(line)) {\n state.stage = 'message';\n\n } else if (state.stage == 'message' && isPath(line)) {\n state.stage = 'path';\n const text = state.value.join('\\n');\n state.result.push(parseCoreError(text));\n state.value = [];\n }\n\n state.value.push(line);\n }\n\n const text = state.value.join('\\n');\n state.result.push(parseCoreError(text));\n\n return state.result;\n}\n\nfunction isPath(line: string): boolean {\n for (const fieldType of ['U', 'I', 'O', 'S', 'OTW', 'D', 'MTW']) {\n if (line.startsWith(`[${fieldType}]`))\n return true;\n }\n\n return false;\n}\n\n/**\n * Parses a suberror from the Pl backend.\n */\nfunction parseCoreError(message: string): PlCoreError {\n // trying to parse a runner or monetization error.\n // https://regex101.com/r/tmKLj7/1\n const runnerErrorRegex = /working directory: \"(.*)\"[\\s\\S]failed to run command: \"([^\"]+)\" exited with code (\\d+)\\.[\\s\\S]*?Here is the latest command output:[\\s\\S]*?\\t([\\s\\S]*)/;\n const match = message.match(runnerErrorRegex);\n if (match) {\n const workingDirectory = match[1];\n const command = match[2];\n const exitCode = parseInt(match[3], 10);\n const stdout = match[4].trim();\n\n if (command.endsWith(`mnz-client`) && exitCode == 1) {\n return new PlMonetizationError(message, command, exitCode, stdout, workingDirectory);\n }\n\n return new PlRunnerError(message, command, exitCode, stdout, workingDirectory);\n }\n\n // trying to parse a Tengo error.\n // https://regex101.com/r/1a7RpO/1\n const workflowErrorRegex = /cannot eval code: cannot eval template: template: (.+)\\n\\t(.*?)\\n\\t(at [\\s\\S]*)/;\n const workflowMatch = message.match(workflowErrorRegex);\n if (workflowMatch) {\n const templateName = workflowMatch[1];\n const errorMessage = workflowMatch[2];\n const stackTrace = workflowMatch[3];\n\n return new PlTengoError(message, templateName, errorMessage, stackTrace);\n }\n\n // if we couldn't parse the error, return a general error.\n return new PlInternalError(message);\n}\n"],"names":["PlErrorReport","fullMessage","plErrorType","plMessage","errors","fieldName","resource","resourceType","__publicField","rt","resourceTypeToString","r","resourceIdToString","f","errType","subErrors","e","PlInternalError","message","PlTengoError","templateName","tengoMessage","tengoStacktrace","PlRunnerError","commandName","exitCode","stdout","workingDirectory","PlMonetizationError","backendErrorSchema","z","parsePlError","error","field","parsed","parseSubErrors","state","line","isPath","text","parseCoreError","fieldType","runnerErrorRegex","match","command","workflowErrorRegex","workflowMatch","errorMessage","stackTrace"],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { ResourceId, ResourceType } from '@milaboratories/pl-client';
|
|
2
|
+
/**
|
|
3
|
+
* A parsed error from the Pl backend.
|
|
4
|
+
* It contains several suberrors, which could be one or different causes of the error.
|
|
5
|
+
*/
|
|
6
|
+
export declare class PlErrorReport extends Error {
|
|
7
|
+
/** Full message from the Pl backend. */
|
|
8
|
+
readonly fullMessage: string;
|
|
9
|
+
/** Either CID conflict or a error from controller. */
|
|
10
|
+
readonly plErrorType: string;
|
|
11
|
+
/** Parsed pl backend message that will be futher parsed into suberrors. */
|
|
12
|
+
readonly plMessage: string;
|
|
13
|
+
/** Could be several different errors, the name is from AggregateError. */
|
|
14
|
+
readonly errors: PlCoreError[];
|
|
15
|
+
/** Optional info about a resource where the error happened. */
|
|
16
|
+
readonly fieldName?: string | undefined;
|
|
17
|
+
readonly resource?: ResourceId | undefined;
|
|
18
|
+
readonly resourceType?: ResourceType | undefined;
|
|
19
|
+
message: string;
|
|
20
|
+
constructor(
|
|
21
|
+
/** Full message from the Pl backend. */
|
|
22
|
+
fullMessage: string,
|
|
23
|
+
/** Either CID conflict or a error from controller. */
|
|
24
|
+
plErrorType: string,
|
|
25
|
+
/** Parsed pl backend message that will be futher parsed into suberrors. */
|
|
26
|
+
plMessage: string,
|
|
27
|
+
/** Could be several different errors, the name is from AggregateError. */
|
|
28
|
+
errors: PlCoreError[],
|
|
29
|
+
/** Optional info about a resource where the error happened. */
|
|
30
|
+
fieldName?: string | undefined, resource?: ResourceId | undefined, resourceType?: ResourceType | undefined);
|
|
31
|
+
toString(): string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A suberror of a parsed error.
|
|
35
|
+
*/
|
|
36
|
+
export type PlCoreError = PlInternalError | PlTengoError | PlRunnerError | PlMonetizationError;
|
|
37
|
+
/**
|
|
38
|
+
* An general error when we couldn't parse the cause.
|
|
39
|
+
*/
|
|
40
|
+
export declare class PlInternalError extends Error {
|
|
41
|
+
readonly message: string;
|
|
42
|
+
constructor(message: string);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Happens when workflow template panics.
|
|
46
|
+
*/
|
|
47
|
+
export declare class PlTengoError extends Error {
|
|
48
|
+
readonly fullMessage: string;
|
|
49
|
+
readonly templateName: string;
|
|
50
|
+
readonly tengoMessage: string;
|
|
51
|
+
readonly tengoStacktrace: string;
|
|
52
|
+
message: string;
|
|
53
|
+
constructor(fullMessage: string, templateName: string, tengoMessage: string, tengoStacktrace: string);
|
|
54
|
+
toString(): string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Happens when a command fails to run.
|
|
58
|
+
*/
|
|
59
|
+
export declare class PlRunnerError extends Error {
|
|
60
|
+
readonly fullMessage: string;
|
|
61
|
+
readonly commandName: string;
|
|
62
|
+
readonly exitCode: number;
|
|
63
|
+
readonly stdout: string;
|
|
64
|
+
readonly workingDirectory: string;
|
|
65
|
+
message: string;
|
|
66
|
+
constructor(fullMessage: string, commandName: string, exitCode: number, stdout: string, workingDirectory: string);
|
|
67
|
+
toString(): string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Happens when a monetization command fails to run.
|
|
71
|
+
*/
|
|
72
|
+
export declare class PlMonetizationError extends PlRunnerError {
|
|
73
|
+
message: string;
|
|
74
|
+
constructor(fullMessage: string, commandName: string, exitCode: number, stdout: string, workingDirectory: string);
|
|
75
|
+
toString(): string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Parses a Pl error and suberrors from the Pl backend.
|
|
79
|
+
*/
|
|
80
|
+
export declare function parsePlError(error: string, resource?: ResourceId, resourceType?: ResourceType, field?: string): PlErrorReport;
|
|
81
|
+
/**
|
|
82
|
+
* Reduces over the lines of the pl error message
|
|
83
|
+
* to extract messages, and categorizes them.
|
|
84
|
+
*/
|
|
85
|
+
export declare function parseSubErrors(message: string): PlCoreError[];
|
|
86
|
+
//# sourceMappingURL=parsed_error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parsed_error.d.ts","sourceRoot":"","sources":["../src/parsed_error.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAG/E,OAAO,EAAE,UAAU,EAAsB,YAAY,EAAwB,MAAM,2BAA2B,CAAC;AAE/G;;;GAGG;AACH,qBAAa,aAAc,SAAQ,KAAK;IAIpC,wCAAwC;aACxB,WAAW,EAAE,MAAM;IAEnC,sDAAsD;aACtC,WAAW,EAAE,MAAM;IAEnC,2EAA2E;aAC3D,SAAS,EAAE,MAAM;IAEjC,0EAA0E;aAC1D,MAAM,EAAE,WAAW,EAAE;IAErC,+DAA+D;aAC/C,SAAS,CAAC,EAAE,MAAM;aAClB,QAAQ,CAAC,EAAE,UAAU;aACrB,YAAY,CAAC,EAAE,YAAY;IAlB7C,OAAO,EAAE,MAAM,CAAC;;IAGd,wCAAwC;IACxB,WAAW,EAAE,MAAM;IAEnC,sDAAsD;IACtC,WAAW,EAAE,MAAM;IAEnC,2EAA2E;IAC3D,SAAS,EAAE,MAAM;IAEjC,0EAA0E;IAC1D,MAAM,EAAE,WAAW,EAAE;IAErC,+DAA+D;IAC/C,SAAS,CAAC,EAAE,MAAM,YAAA,EAClB,QAAQ,CAAC,EAAE,UAAU,YAAA,EACrB,YAAY,CAAC,EAAE,YAAY,YAAA;IAO7C,QAAQ;CAWT;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GACnB,eAAe,GACf,YAAY,GACZ,aAAa,GACb,mBAAmB,CAAC;AAExB;;GAEG;AACH,qBAAa,eAAgB,SAAQ,KAAK;aAEtB,OAAO,EAAE,MAAM;gBAAf,OAAO,EAAE,MAAM;CAKlC;AAED;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;aAInB,WAAW,EAAE,MAAM;aACnB,YAAY,EAAE,MAAM;aACpB,YAAY,EAAE,MAAM;aACpB,eAAe,EAAE,MAAM;IANzC,OAAO,EAAE,MAAM,CAAC;gBAGE,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,MAAM;IAOzC,QAAQ;CAWT;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;aAIpB,WAAW,EAAE,MAAM;aACnB,WAAW,EAAE,MAAM;aACnB,QAAQ,EAAE,MAAM;aAChB,MAAM,EAAE,MAAM;aACd,gBAAgB,EAAE,MAAM;IAP1C,OAAO,EAAE,MAAM,CAAC;gBAGE,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM;IAO1C,QAAQ;CAWT;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,aAAa;IACpD,OAAO,EAAE,MAAM,CAAC;gBAGd,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM;IAO1B,QAAQ;CAWT;AAUD;;GAEG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EAEb,QAAQ,CAAC,EAAE,UAAU,EACrB,YAAY,CAAC,EAAE,YAAY,EAC3B,KAAK,CAAC,EAAE,MAAM,GACb,aAAa,CAcf;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,CA+B7D"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@milaboratories/pl-errors",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Parsing errors from Pl backend",
|
|
5
|
+
"types": "./dist/index.d.ts",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"import": "./dist/index.mjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"./dist/**/*",
|
|
17
|
+
"./src/**/*"
|
|
18
|
+
],
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"zod": "~3.23.8",
|
|
21
|
+
"@milaboratories/pl-client": "2.7.12"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"typescript": "~5.5.4",
|
|
25
|
+
"vite": "^5.4.11",
|
|
26
|
+
"vitest": "^2.1.8",
|
|
27
|
+
"@milaboratories/platforma-build-configs": "1.0.2",
|
|
28
|
+
"@milaboratories/eslint-config": "^1.0.1"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"type-check": "tsc --noEmit --composite false",
|
|
32
|
+
"build": "vite build",
|
|
33
|
+
"test": "vitest",
|
|
34
|
+
"do-pack": "rm -f *.tgz && pnpm pack && mv *.tgz package.tgz"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './parsed_error';
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ResourceId, resourceIdFromString, stringifyWithResourceId } from "@milaboratories/pl-client";
|
|
2
|
+
import { parsePlError, parseSubErrors, PlMonetizationError, PlErrorReport, PlRunnerError, PlTengoError } from './parsed_error';
|
|
3
|
+
import { describe, it, expect } from 'vitest';
|
|
4
|
+
|
|
5
|
+
const runnerError = '{"errorType":"","message":"\\"NG:0x2331A5\\" has 1 input errors:\\n[I] \\"NG:0x2331A5/blob\\": \\"NG:0x2331A4\\" has 1 input errors:\\n[I] \\"NG:0x2331A4/resource\\": \\"NG:0x2331CD\\" has 1 input errors:\\n[I] \\"NG:0x2331CD/resource\\": \\"NG:0x2331CA\\" has 1 input errors:\\n[I] \\"NG:0x2331CA/inputs\\": \\"NG:0x2331CB\\" has 1 input errors:\\n[I] \\"NG:0x2331CB/workdir\\": working directory: \\"workdirs/0x2331E0\\"\\nfailed to run command: \\"java\\" exited with code 22.\\nHere is the latest command output:\\n\\tLicense manager thread died.\\n\\t=== No License ===\\n\\t\\n\\tTo use MiXCR, please, provide a valid license.\\n\\t\\n\\tIf you already have a license, activate it by calling:\\n\\t mixcr activate-license\\n\\t\\n\\t\\n"}'
|
|
6
|
+
|
|
7
|
+
const tengoError = "{\"errorType\":\"\",\"message\":\"\\\"NG:0x16A\\\" has 1 input errors:\\n[I] \\\"NG:0x16A/resource\\\": cannot eval code: cannot eval template: template: @platforma-open/milaboratories.samples-and-data.workflow:main@1.10.0\\n\\tRuntime Error: File handle not set for \\\"R1\\\" in sample \\\"S63UG7K2IRZSSMAI4UVB5CNJ\\\"\\n\\tat @platforma-sdk/workflow-tengo:ll:25:1\\n\\tat @platforma-open/milaboratories.samples-and-data.workflow:main:205:7\\n\\tat @platforma-sdk/workflow-tengo:workflow:264:11\\n\\tat @platforma-sdk/workflow-tengo:tpl:470:11\\n\\tat @platforma-sdk/workflow-tengo:tpl:373:1\\n\\tat @platforma-sdk/workflow-tengo:workflow:261:1\\n\\tat @platforma-open/milaboratories.samples-and-data.workflow:main:35:1\"}"
|
|
8
|
+
|
|
9
|
+
const monetizationSubErrors = `"NG:0x1F94C0" has 1 input errors:
|
|
10
|
+
[I] "NG:0x1F94C0/resource": "NG:0x1F94FA" has 1 input errors:
|
|
11
|
+
[I] "NG:0x1F94FA/resource": "NG:0x1F94F5" has 1 input errors:
|
|
12
|
+
[I] "NG:0x1F94F5/inputs": "NG:0x1F94F6" has 1 input errors:
|
|
13
|
+
[I] "NG:0x1F94F6/workdir": "NG:0x1F94F0" has 2 input errors:
|
|
14
|
+
[I] "NG:0x1F94F0/refs": "NG:0x1F94F1" has 1 input errors:
|
|
15
|
+
[I] "NG:0x1F94F1/monetization": "NG:0x1F94EE" has 1 input errors:
|
|
16
|
+
[I] "NG:0x1F94EE/resource": "NG:0x1F94EC" has 1 input errors:
|
|
17
|
+
[I] "NG:0x1F94EC/inputs": "NG:0x1F94ED" has 1 input errors:
|
|
18
|
+
[I] "NG:0x1F94ED/resource": "NG:0x1F94EB" has 1 input errors:
|
|
19
|
+
[I] "NG:0x1F94EB/resource": "NG:0x1F94E6" has 1 input errors:
|
|
20
|
+
[I] "NG:0x1F94E6/resource": "NG:0x1F94E2" has 1 input errors:
|
|
21
|
+
[I] "NG:0x1F94E2/inputs": "NG:0x1F94E3" has 1 input errors:
|
|
22
|
+
[I] "NG:0x1F94E3/workdir": working directory: "workdirs/0x1F9514"
|
|
23
|
+
failed to run command: "/home/snyssfx/PlatformaDev/local/packages/installed/platforma-open/platforma-open/milaboratories.software-small-binaries.mnz-client/main/1.5.9-linux-x64.0x1F1A04/mnz-client" exited with code 1.
|
|
24
|
+
Here is the latest command output:
|
|
25
|
+
2025/03/13 17:25:18 get API error: VALIDATION_ERR Invalid /mnz/run-spec body: field productKey -> Invalid product key
|
|
26
|
+
[I] "NG:0x1F94F0/workdirIn": "NG:0x1F94E9" has 1 input errors:
|
|
27
|
+
[O] "NG:0x1F94E9/resource": "NG:0x1F94E2" has 1 input errors:
|
|
28
|
+
[U] "NG:0x1F94E2/inputs": "NG:0x1F94E3" has 1 input errors:
|
|
29
|
+
[MTW] "NG:0x1F94E3/workdir": working directory: "workdirs/0x1F9514"
|
|
30
|
+
failed to run command: "/home/snyssfx/PlatformaDev/local/packages/installed/platforma-open/platforma-open/milaboratories.software-small-binaries.mnz-client/main/1.5.9-linux-x64.0x1F1A04/mnz-client" exited with code 1.
|
|
31
|
+
Here is the latest command output:
|
|
32
|
+
2025/03/13 17:25:18 get API error: VALIDATION_ERR Invalid /mnz/run-spec body: field productKey -> Invalid product key`
|
|
33
|
+
|
|
34
|
+
describe('parsePlError', () => {
|
|
35
|
+
it('should parse runner error correctly', () => {
|
|
36
|
+
const result = parsePlError(runnerError, resourceIdFromString('NG:0x2331A5')! as ResourceId, { name: 'RunCommand', version: "1" }, 'fieldName',);
|
|
37
|
+
|
|
38
|
+
expect(result).toBeInstanceOf(PlErrorReport);
|
|
39
|
+
expect(result.name).toBe('PlErrorReport');
|
|
40
|
+
expect(result.plErrorType).toBe('');
|
|
41
|
+
expect(result.fieldName).toBe('fieldName');
|
|
42
|
+
expect(stringifyWithResourceId(result.resource)).toBe('\"NG:0x2331a5\"');
|
|
43
|
+
|
|
44
|
+
expect(result.errors.length).toBe(1);
|
|
45
|
+
expect(result.errors[0]).toBeInstanceOf(PlRunnerError);
|
|
46
|
+
expect((result.errors[0] as PlRunnerError).commandName).toBe('java');
|
|
47
|
+
expect((result.errors[0] as PlRunnerError).exitCode).toBe(22);
|
|
48
|
+
expect((result.errors[0] as PlRunnerError).stdout).toBeDefined();
|
|
49
|
+
expect((result.errors[0] as PlRunnerError).workingDirectory).toBe('workdirs/0x2331E0');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should parse workflow error correctly', () => {
|
|
53
|
+
const result = parsePlError(tengoError, resourceIdFromString('NG:0x16A')! as ResourceId, { name: 'RunCommand', version: "1" }, 'fieldName');
|
|
54
|
+
|
|
55
|
+
expect(result).toBeInstanceOf(PlErrorReport);
|
|
56
|
+
expect(result.name).toBe('PlErrorReport');
|
|
57
|
+
expect(result.plErrorType).toBe('');
|
|
58
|
+
expect(result.fieldName).toBe('fieldName');
|
|
59
|
+
expect(stringifyWithResourceId(result.resource)).toBe('\"NG:0x16a\"');
|
|
60
|
+
|
|
61
|
+
expect(result.errors.length).toBe(1);
|
|
62
|
+
expect(result.errors[0]).toBeInstanceOf(PlTengoError);
|
|
63
|
+
expect((result.errors[0] as PlTengoError).templateName).toBe('@platforma-open/milaboratories.samples-and-data.workflow:main@1.10.0');
|
|
64
|
+
expect((result.errors[0] as PlTengoError).tengoMessage).toBe('Runtime Error: File handle not set for "R1" in sample "S63UG7K2IRZSSMAI4UVB5CNJ"');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should parse monetization sub errors correctly', () => {
|
|
68
|
+
const result = parseSubErrors(monetizationSubErrors);
|
|
69
|
+
|
|
70
|
+
expect(result.length).toBe(2);
|
|
71
|
+
|
|
72
|
+
expect(result[0]).toBeInstanceOf(PlMonetizationError);
|
|
73
|
+
expect((result[0] as PlMonetizationError).commandName).toContain('mnz-client');
|
|
74
|
+
expect((result[0] as PlMonetizationError).exitCode).toBe(1);
|
|
75
|
+
expect((result[0] as PlMonetizationError).stdout).toBeDefined();
|
|
76
|
+
expect((result[0] as PlMonetizationError).workingDirectory).toBe('workdirs/0x1F9514');
|
|
77
|
+
|
|
78
|
+
expect(result[1]).toBeInstanceOf(PlMonetizationError);
|
|
79
|
+
expect((result[1] as PlMonetizationError).commandName).toContain('mnz-client');
|
|
80
|
+
expect((result[1] as PlMonetizationError).exitCode).toBe(1);
|
|
81
|
+
expect((result[1] as PlMonetizationError).stdout).toBeDefined();
|
|
82
|
+
expect((result[1] as PlMonetizationError).workingDirectory).toBe('workdirs/0x1F9514');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/** Pl Backend throws arbitrary errors, and we're trying to parse them here. */
|
|
2
|
+
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { ResourceId, resourceIdToString, ResourceType, resourceTypeToString } from "@milaboratories/pl-client";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A parsed error from the Pl backend.
|
|
8
|
+
* It contains several suberrors, which could be one or different causes of the error.
|
|
9
|
+
*/
|
|
10
|
+
export class PlErrorReport extends Error {
|
|
11
|
+
message: string;
|
|
12
|
+
|
|
13
|
+
constructor(
|
|
14
|
+
/** Full message from the Pl backend. */
|
|
15
|
+
public readonly fullMessage: string,
|
|
16
|
+
|
|
17
|
+
/** Either CID conflict or a error from controller. */
|
|
18
|
+
public readonly plErrorType: string,
|
|
19
|
+
|
|
20
|
+
/** Parsed pl backend message that will be futher parsed into suberrors. */
|
|
21
|
+
public readonly plMessage: string,
|
|
22
|
+
|
|
23
|
+
/** Could be several different errors, the name is from AggregateError. */
|
|
24
|
+
public readonly errors: PlCoreError[],
|
|
25
|
+
|
|
26
|
+
/** Optional info about a resource where the error happened. */
|
|
27
|
+
public readonly fieldName?: string,
|
|
28
|
+
public readonly resource?: ResourceId,
|
|
29
|
+
public readonly resourceType?: ResourceType,
|
|
30
|
+
) {
|
|
31
|
+
super(fullMessage);
|
|
32
|
+
this.name = 'PlErrorReport';
|
|
33
|
+
this.message = this.toString();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
toString() {
|
|
37
|
+
const rt = this.resourceType ? `${resourceTypeToString(this.resourceType)},` : '';
|
|
38
|
+
const r = this.resource ? resourceIdToString(this.resource) : '';
|
|
39
|
+
const f = this.fieldName ? `/${this.fieldName}` : '';
|
|
40
|
+
const errType = this.plErrorType ? `error type: ${this.plErrorType}` : '';
|
|
41
|
+
const subErrors = this.errors.map(e => e.message).join('\n\n');
|
|
42
|
+
|
|
43
|
+
return `PlErrorReport: resource: ${rt} ${r}${f}
|
|
44
|
+
${errType}
|
|
45
|
+
${subErrors}`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A suberror of a parsed error.
|
|
51
|
+
*/
|
|
52
|
+
export type PlCoreError =
|
|
53
|
+
| PlInternalError
|
|
54
|
+
| PlTengoError
|
|
55
|
+
| PlRunnerError
|
|
56
|
+
| PlMonetizationError;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* An general error when we couldn't parse the cause.
|
|
60
|
+
*/
|
|
61
|
+
export class PlInternalError extends Error {
|
|
62
|
+
constructor(
|
|
63
|
+
public readonly message: string,
|
|
64
|
+
) {
|
|
65
|
+
super(message);
|
|
66
|
+
this.name = 'PlInternalError';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Happens when workflow template panics.
|
|
72
|
+
*/
|
|
73
|
+
export class PlTengoError extends Error {
|
|
74
|
+
message: string;
|
|
75
|
+
|
|
76
|
+
constructor(
|
|
77
|
+
public readonly fullMessage: string,
|
|
78
|
+
public readonly templateName: string,
|
|
79
|
+
public readonly tengoMessage: string,
|
|
80
|
+
public readonly tengoStacktrace: string,
|
|
81
|
+
) {
|
|
82
|
+
super(fullMessage);
|
|
83
|
+
this.name = 'PlWorkflowError';
|
|
84
|
+
this.message = this.toString();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
toString() {
|
|
88
|
+
return `PlWorkflowError:
|
|
89
|
+
template: ${this.templateName}
|
|
90
|
+
message:
|
|
91
|
+
${this.tengoMessage}
|
|
92
|
+
tengo stacktrace:
|
|
93
|
+
${this.tengoStacktrace}
|
|
94
|
+
|
|
95
|
+
full message:
|
|
96
|
+
${this.fullMessage}`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Happens when a command fails to run.
|
|
102
|
+
*/
|
|
103
|
+
export class PlRunnerError extends Error {
|
|
104
|
+
message: string;
|
|
105
|
+
|
|
106
|
+
constructor(
|
|
107
|
+
public readonly fullMessage: string,
|
|
108
|
+
public readonly commandName: string,
|
|
109
|
+
public readonly exitCode: number,
|
|
110
|
+
public readonly stdout: string,
|
|
111
|
+
public readonly workingDirectory: string,
|
|
112
|
+
) {
|
|
113
|
+
super(fullMessage);
|
|
114
|
+
this.name = 'PlRunnerError';
|
|
115
|
+
this.message = this.toString();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
toString() {
|
|
119
|
+
return `PlRunnerError:
|
|
120
|
+
command: ${this.commandName}
|
|
121
|
+
exit code: ${this.exitCode}
|
|
122
|
+
working directory: ${this.workingDirectory}
|
|
123
|
+
stdout:
|
|
124
|
+
${this.stdout}
|
|
125
|
+
|
|
126
|
+
full message:
|
|
127
|
+
${this.fullMessage}`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Happens when a monetization command fails to run.
|
|
133
|
+
*/
|
|
134
|
+
export class PlMonetizationError extends PlRunnerError {
|
|
135
|
+
message: string;
|
|
136
|
+
|
|
137
|
+
constructor(
|
|
138
|
+
fullMessage: string,
|
|
139
|
+
commandName: string,
|
|
140
|
+
exitCode: number,
|
|
141
|
+
stdout: string,
|
|
142
|
+
workingDirectory: string,
|
|
143
|
+
) {
|
|
144
|
+
super(fullMessage, commandName, exitCode, stdout, workingDirectory);
|
|
145
|
+
this.name = 'PlMonetizationError';
|
|
146
|
+
this.message = this.toString();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
toString() {
|
|
150
|
+
return `PlMonetizationError:
|
|
151
|
+
command: ${this.commandName}
|
|
152
|
+
exit code: ${this.exitCode}
|
|
153
|
+
working directory: ${this.workingDirectory}
|
|
154
|
+
stdout:
|
|
155
|
+
${this.stdout}
|
|
156
|
+
|
|
157
|
+
full message:
|
|
158
|
+
${this.fullMessage}`;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* How the Pl backend represents an error.
|
|
164
|
+
*/
|
|
165
|
+
const backendErrorSchema = z.object({
|
|
166
|
+
errorType: z.string(),
|
|
167
|
+
message: z.string(),
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Parses a Pl error and suberrors from the Pl backend.
|
|
172
|
+
*/
|
|
173
|
+
export function parsePlError(
|
|
174
|
+
error: string,
|
|
175
|
+
|
|
176
|
+
resource?: ResourceId,
|
|
177
|
+
resourceType?: ResourceType,
|
|
178
|
+
field?: string,
|
|
179
|
+
): PlErrorReport {
|
|
180
|
+
const parsed = backendErrorSchema.parse(JSON.parse(error));
|
|
181
|
+
const subErrors = parseSubErrors(parsed.message);
|
|
182
|
+
|
|
183
|
+
return new PlErrorReport(
|
|
184
|
+
error,
|
|
185
|
+
parsed.errorType,
|
|
186
|
+
parsed.message,
|
|
187
|
+
subErrors,
|
|
188
|
+
|
|
189
|
+
field,
|
|
190
|
+
resource,
|
|
191
|
+
resourceType,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Reduces over the lines of the pl error message
|
|
197
|
+
* to extract messages, and categorizes them.
|
|
198
|
+
*/
|
|
199
|
+
export function parseSubErrors(message: string): PlCoreError[] {
|
|
200
|
+
// the state of this reducing function
|
|
201
|
+
const state = {
|
|
202
|
+
stage: 'initial' as 'initial' | 'path' | 'message',
|
|
203
|
+
value: [] as string[],
|
|
204
|
+
result: [] as PlCoreError[],
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
for (const line of message.split('\n')) {
|
|
208
|
+
if (state.stage == 'initial') {
|
|
209
|
+
// we need initial stage because apparently the first line
|
|
210
|
+
// of the error doesn't have [I], but is a path line.
|
|
211
|
+
state.stage = 'path';
|
|
212
|
+
|
|
213
|
+
} else if (state.stage == 'path' && !isPath(line)) {
|
|
214
|
+
state.stage = 'message';
|
|
215
|
+
|
|
216
|
+
} else if (state.stage == 'message' && isPath(line)) {
|
|
217
|
+
state.stage = 'path';
|
|
218
|
+
const text = state.value.join('\n');
|
|
219
|
+
state.result.push(parseCoreError(text));
|
|
220
|
+
state.value = [];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
state.value.push(line);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const text = state.value.join('\n');
|
|
227
|
+
state.result.push(parseCoreError(text));
|
|
228
|
+
|
|
229
|
+
return state.result;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function isPath(line: string): boolean {
|
|
233
|
+
for (const fieldType of ['U', 'I', 'O', 'S', 'OTW', 'D', 'MTW']) {
|
|
234
|
+
if (line.startsWith(`[${fieldType}]`))
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Parses a suberror from the Pl backend.
|
|
243
|
+
*/
|
|
244
|
+
function parseCoreError(message: string): PlCoreError {
|
|
245
|
+
// trying to parse a runner or monetization error.
|
|
246
|
+
// https://regex101.com/r/tmKLj7/1
|
|
247
|
+
const runnerErrorRegex = /working directory: "(.*)"[\s\S]failed to run command: "([^"]+)" exited with code (\d+)\.[\s\S]*?Here is the latest command output:[\s\S]*?\t([\s\S]*)/;
|
|
248
|
+
const match = message.match(runnerErrorRegex);
|
|
249
|
+
if (match) {
|
|
250
|
+
const workingDirectory = match[1];
|
|
251
|
+
const command = match[2];
|
|
252
|
+
const exitCode = parseInt(match[3], 10);
|
|
253
|
+
const stdout = match[4].trim();
|
|
254
|
+
|
|
255
|
+
if (command.endsWith(`mnz-client`) && exitCode == 1) {
|
|
256
|
+
return new PlMonetizationError(message, command, exitCode, stdout, workingDirectory);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return new PlRunnerError(message, command, exitCode, stdout, workingDirectory);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// trying to parse a Tengo error.
|
|
263
|
+
// https://regex101.com/r/1a7RpO/1
|
|
264
|
+
const workflowErrorRegex = /cannot eval code: cannot eval template: template: (.+)\n\t(.*?)\n\t(at [\s\S]*)/;
|
|
265
|
+
const workflowMatch = message.match(workflowErrorRegex);
|
|
266
|
+
if (workflowMatch) {
|
|
267
|
+
const templateName = workflowMatch[1];
|
|
268
|
+
const errorMessage = workflowMatch[2];
|
|
269
|
+
const stackTrace = workflowMatch[3];
|
|
270
|
+
|
|
271
|
+
return new PlTengoError(message, templateName, errorMessage, stackTrace);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// if we couldn't parse the error, return a general error.
|
|
275
|
+
return new PlInternalError(message);
|
|
276
|
+
}
|