@ddex-workbench/sdk
Version:
Official SDK for DDEX Workbench - Open-source DDEX validation and processing tools
9 lines (8 loc) • 11.6 kB
JavaScript
import E from"axios";var l=class s extends Error{constructor(t,r,i){super(t);this.code=r;this.details=i;this.name="DDEXError",Object.setPrototypeOf(this,s.prototype)}},f=class s extends l{constructor(t,r){super(t,"RATE_LIMIT_EXCEEDED");this.retryAfter=r;this.name="RateLimitError",Object.setPrototypeOf(this,s.prototype)}getRetryMessage(){return`Rate limit exceeded. Please retry after ${this.retryAfter} seconds.`}},R=class s extends l{constructor(e){super(e,"AUTHENTICATION_FAILED"),this.name="AuthenticationError",Object.setPrototypeOf(this,s.prototype)}},p=class s extends l{constructor(t,r){super(t,"VALIDATION_ERROR");this.validationErrors=r;this.name="ValidationError",Object.setPrototypeOf(this,s.prototype)}getSummary(){if(!this.validationErrors||this.validationErrors.length===0)return this.message;let t=this.validationErrors.map(r=>r.field?`${r.field}: ${r.message}`:r.message).join(", ");return`${this.message}: ${t}`}},m=class s extends l{constructor(t,r){super(t,"NETWORK_ERROR");this.statusCode=r;this.name="NetworkError",Object.setPrototypeOf(this,s.prototype)}isRetryable(){return this.statusCode?this.statusCode>=500&&this.statusCode<600:!0}},V=class s extends l{constructor(t,r){super(t,"NOT_FOUND");this.resource=r;this.name="NotFoundError",Object.setPrototypeOf(this,s.prototype)}},v=class s extends l{constructor(t,r){super(t,"CONFIGURATION_ERROR");this.field=r;this.name="ConfigurationError",Object.setPrototypeOf(this,s.prototype)}},w=class s extends l{constructor(t,r){super(t,"TIMEOUT");this.timeout=r;this.name="TimeoutError",Object.setPrototypeOf(this,s.prototype)}},P=class s extends l{constructor(t,r){super(t,"FILE_ERROR");this.filePath=r;this.name="FileError",Object.setPrototypeOf(this,s.prototype)}},x=class s extends l{constructor(t,r,i){super(t,"PARSE_ERROR");this.line=r;this.column=i;this.name="ParseError",Object.setPrototypeOf(this,s.prototype)}getLocation(){return this.line!==void 0&&this.column!==void 0?`Line ${this.line}, Column ${this.column}`:this.line!==void 0?`Line ${this.line}`:"Unknown location"}},b=class s extends l{constructor(t,r,i){super(t,`API_ERROR_${r}`);this.statusCode=r;this.response=i;this.name="APIError",Object.setPrototypeOf(this,s.prototype)}isClientError(){return this.statusCode>=400&&this.statusCode<500}isServerError(){return this.statusCode>=500&&this.statusCode<600}};var y=class{constructor(e){this.client=e}async validateAuto(e,t){let r=this.detectVersion(e);if(!r)throw new Error("Could not detect ERN version from XML content");return this.client.validate(e,{version:r,profile:t})}async validateWithSVRL(e,t){return this.client.validateWithSVRL(e,t)}async validateBatch(e,t){return this.client.validateBatch(e,t)}async isValid(e,t){return(await this.client.validate(e,t)).valid}async getErrors(e,t){return(await this.client.validate(e,t)).errors}getCriticalErrors(e){return e.errors.filter(t=>t.severity==="error"||t.severity==="fatal")}getSchematronErrors(e){return e.errors.filter(t=>t.rule&&t.rule.startsWith("Schematron-"))}getXSDErrors(e){return e.errors.filter(t=>t.rule&&(t.rule.startsWith("XSD-")||t.rule==="XSD"))}getBusinessRuleErrors(e){return e.errors.filter(t=>t.rule&&t.rule.startsWith("BusinessRule-"))}parseSVRL(e){let t=(e.match(/<svrl:successful-report/g)||[]).length,r=(e.match(/<svrl:failed-assert/g)||[]).length,i=(e.match(/role="warning"/g)||[]).length,n=(e.match(/<svrl:fired-rule/g)||[]).length,a=e.match(/profile="([^"]+)"/),o=e.match(/schemaVersion="([^"]+)"/);return{assertions:t,failures:r,warnings:i,firedRules:n,profile:a?a[1]:void 0,schemaVersion:o?o[1]:void 0}}async getProfileCompliance(e,t,r){var c;let i=await this.client.validate(e,{version:t,profile:r,generateSVRL:!0,verbose:!0}),n=((c=i.passedRules)==null?void 0:c.length)||0,a=i.errors.filter(d=>d.rule&&d.rule.startsWith("Schematron-")).length,o=n+a,u=o>0?n/o*100:0;return{compliant:i.valid,profile:r,version:t,errors:i.errors.length,warnings:i.warnings.length,passedRules:n,failedRules:a,complianceRate:Math.round(u*100)/100,svrlAvailable:i.svrl!==void 0,details:i.passedRules}}generateSummary(e){var u;let t=((u=e.passedRules)==null?void 0:u.length)||0,r=e.errors.filter(c=>c.rule).length,i=t+r,n=i>0?t/i*100:0,a=this.getXSDErrors(e).length,o=this.getSchematronErrors(e).length;return{totalRules:i,passedRules:t,failedRules:r,complianceRate:Math.round(n*100)/100,profileCompliant:o===0,schemaCompliant:a===0}}formatErrors(e,t={}){let r=t.maxErrors?e.slice(0,t.maxErrors):e;if(t.groupByRule){let i=this.groupErrorsByRule(r);return Object.entries(i).map(([n,a])=>{let o=a.map(u=>this.formatSingleError(u,t)).join(`
`);return`${n} (${a.length} errors):
${o}`}).join(`
`)}return r.map(i=>this.formatSingleError(i,t)).join(`
`)}formatSingleError(e,t){let r=`Line ${e.line}:${e.column} - ${e.message}`;return t.includeContext&&e.context&&(r+=` (Context: ${e.context})`),t.includeSuggestions&&e.suggestion&&(r+=`
Suggestion: ${e.suggestion}`),r}groupErrorsByRule(e){return e.reduce((t,r)=>{let i=r.rule||"Unknown";return t[i]||(t[i]=[]),t[i].push(r),t},{})}detectVersion(e){return e.includes('xmlns:ern="http://ddex.net/xml/ern/43"')||e.includes('MessageSchemaVersionId="ern/43"')||e.includes("/ern/43")?"4.3":e.includes('xmlns:ern="http://ddex.net/xml/ern/42"')||e.includes('MessageSchemaVersionId="ern/42"')||e.includes("/ern/42")?"4.2":e.includes('xmlns:ern="http://ddex.net/xml/ern/382"')||e.includes('MessageSchemaVersionId="ern/382"')||e.includes("/ern/382")||e.includes('xmlns:ernm="http://ddex.net/xml/ern/382"')||e.includes('xmlns:ern="http://ddex.net/xml/ern/38"')?"3.8.2":null}detectProfile(e){let t={AudioAlbum:[/ReleaseProfileVersionId="[^"]*AudioAlbum/i,/Profile[^>]*AudioAlbum/i],AudioSingle:[/ReleaseProfileVersionId="[^"]*AudioSingle/i,/Profile[^>]*AudioSingle/i],Video:[/ReleaseProfileVersionId="[^"]*Video/i,/Profile[^>]*Video/i],Mixed:[/ReleaseProfileVersionId="[^"]*Mixed/i,/Profile[^>]*Mixed/i],Classical:[/ReleaseProfileVersionId="[^"]*Classical/i,/Profile[^>]*Classical/i],Ringtone:[/ReleaseProfileVersionId="[^"]*Ringtone/i,/Profile[^>]*Ringtone/i],DJ:[/ReleaseProfileVersionId="[^"]*DJ/i,/Profile[^>]*DJ/i],ReleaseByRelease:[/ReleaseProfileVersionId="[^"]*ReleaseByRelease/i,/Profile[^>]*ReleaseByRelease/i]};for(let[r,i]of Object.entries(t))if(i.some(n=>n.test(e)))return r;return null}extractMetadata(e){let t={};t.version=this.detectVersion(e)||void 0,t.profile=this.detectProfile(e)||void 0;let r=e.match(/MessageId>([^<]+)</);r&&(t.messageId=r[1]);let i=e.match(/MessageCreatedDateTime>([^<]+)</);i&&(t.createdDate=i[1]);let n=e.match(/<Release[^>]*>/g);return n&&(t.releaseCount=n.length),t}};var D=class{constructor(e={}){this.config={baseURL:e.baseURL||"https://api.ddex-workbench.org/v1",apiKey:e.apiKey,timeout:e.timeout||3e4,environment:e.environment||"production",maxRetries:e.maxRetries||3,retryDelay:e.retryDelay||1e3,...e},this.client=E.create({baseURL:this.config.baseURL,timeout:this.config.timeout,headers:{"Content-Type":"application/json","User-Agent":`ddex-workbench-sdk/1.1.0 (${this.getEnvironment()})`,...this.config.headers}}),this.config.apiKey&&(this.client.defaults.headers.common["X-API-Key"]=this.config.apiKey),this.setupInterceptors(),this.validator=new y(this)}async validate(e,t){try{return(await this.retryableRequest(()=>this.client.post("/validate",{content:e,type:"ERN",version:t.version,profile:t.profile,generateSVRL:t.generateSVRL,verbose:t.verbose,customRules:t.customRules}))).data}catch(r){throw this.handleError(r)}}async validateWithSVRL(e,t){return this.validate(e,{...t,generateSVRL:!0})}async validateFile(e,t){try{if(typeof window!="undefined")throw new Error("File validation is not supported in browser environment");let r=await import("fs/promises"),i=await import("path"),n=await r.readFile(e,t.encoding||"utf-8"),a=await r.stat(e),o=await this.validate(n.toString(),t);if(o.metadata.fileInfo={name:i.basename(e),size:a.size},t.includeHash){let c=(await import("crypto")).createHash("sha256").update(n).digest("hex");o.metadata.fileInfo.hash=c}return o}catch(r){throw this.handleError(r)}}async validateURL(e,t){try{let r=await E.get(e,{timeout:t.fetchTimeout||this.config.timeout,headers:t.headers,responseType:"text"});return this.validate(r.data,t)}catch(r){throw this.handleError(r)}}async validateBatch(e,t={}){let r=Date.now(),i=t.concurrency||3,n=[],a=0,o=0;for(let u=0;u<e.length;u+=i){let c=e.slice(u,u+i),d=await Promise.all(c.map(async g=>{try{let h=await this.validate(g.content,g.options);return h.valid&&a++,o++,t.onProgress&&t.onProgress(o,e.length),{filename:g.filename||`file_${o}`,result:h}}catch(h){if(o++,t.onProgress&&t.onProgress(o,e.length),t.stopOnError)throw h;let S=h instanceof Error?h.message:"Unknown error";return{filename:g.filename||`file_${o}`,result:{valid:!1,errors:[{line:0,column:0,message:S,severity:"fatal"}],warnings:[],metadata:{processingTime:0,schemaVersion:g.options.version,validatedAt:new Date().toISOString(),errorCount:1,warningCount:0,validationSteps:[]}}}}}));n.push(...d)}return{totalFiles:e.length,validFiles:a,invalidFiles:e.length-a,results:n,processingTime:Date.now()-r}}async getSupportedFormats(){try{return(await this.client.get("/formats")).data}catch(e){throw this.handleError(e)}}async checkHealth(){try{let e=Date.now();return{...(await this.client.get("/health")).data,responseTime:Date.now()-e}}catch(e){throw this.handleError(e)}}async listApiKeys(){try{return(await this.client.get("/keys")).data}catch(e){throw this.handleError(e)}}async createApiKey(e){try{return(await this.client.post("/keys",{name:e})).data}catch(t){throw this.handleError(t)}}async revokeApiKey(e){try{await this.client.delete(`/keys/${e}`)}catch(t){throw this.handleError(t)}}setupInterceptors(){this.client.interceptors.response.use(e=>e,e=>Promise.reject(e))}async retryableRequest(e,t=this.config.maxRetries||3){try{return await e()}catch(r){if(t>0&&this.shouldRetry(r))return await this.delay(this.config.retryDelay||1e3),this.retryableRequest(e,t-1);throw r}}shouldRetry(e){var r;if(!E.isAxiosError(e))return!1;let t=(r=e.response)==null?void 0:r.status;return!t||t>=500}delay(e){return new Promise(t=>setTimeout(t,e))}handleError(e){var n,a,o,u,c;if(!E.isAxiosError(e))return e instanceof Error?e:new Error(String(e));let t=e,r=((o=(a=(n=t.response)==null?void 0:n.data)==null?void 0:a.error)==null?void 0:o.message)||t.message||"An unknown error occurred";switch((u=t.response)==null?void 0:u.status){case 401:return new R(r);case 429:let d=parseInt(((c=t.response)==null?void 0:c.headers["retry-after"])||"60");return new f(r,d);case 422:return new p(r);case 400:return new p(r);case 404:return new l(`Resource not found: ${r}`);case 500:case 502:case 503:case 504:return new m(`Server error: ${r}`);default:return t.response?new l(r):new m(`Network error: ${r}`)}}getEnvironment(){var e;return typeof window!="undefined"?`browser/${this.config.environment}`:typeof process!="undefined"&&((e=process.versions)!=null&&e.node)?`node/${process.version}/${this.config.environment}`:this.config.environment||"unknown"}setApiKey(e){this.config.apiKey=e,this.client.defaults.headers.common["X-API-Key"]=e}clearApiKey(){this.config.apiKey=void 0,delete this.client.defaults.headers.common["X-API-Key"]}getConfig(){return{...this.config}}};export{b as APIError,R as AuthenticationError,v as ConfigurationError,D as DDEXClient,l as DDEXError,y as DDEXValidator,P as FileError,m as NetworkError,V as NotFoundError,x as ParseError,f as RateLimitError,w as TimeoutError,p as ValidationError};
if (typeof window !== 'undefined' && !window.DDEXWorkbench) { window.DDEXWorkbench = DDEXWorkbench; }
//# sourceMappingURL=index.mjs.map