@appetize/playwright
Advanced tools
Comparing version 1.0.1-beta.1 to 1.0.1
@@ -18,5 +18,5 @@ var ie=(d,w,b)=>{if(!w.has(d))throw TypeError("Cannot "+b)};var A=(d,w,b)=>(ie(d,w,"read from private field"),b?b.call(d):w.get(d)),j=(d,w,b)=>{if(w.has(d))throw TypeError("Cannot add the same private member more than once");w instanceof WeakSet?w.add(d):w.set(d,b)},B=(d,w,b,O)=>(ie(d,w,"write to private field"),O?O.call(d,b):w.set(d,b),b);(function(d,w){typeof exports=="object"&&typeof module!="undefined"?w(exports,require("events"),require("@playwright/test"),require("fs")):typeof define=="function"&&define.amd?define(["exports","events","@playwright/test","fs"],w):(d=typeof globalThis!="undefined"?globalThis:d||self,w(d.playwright={},d.events,d.test$1,d.fs))})(this,function(d,w,b,O){var k,$;"use strict";function ne(i){return i&&typeof i=="object"&&"default"in i?i:{default:i}}var I=ne(O);async function re(i,{retries:e=3,timeout:t=1e3,predicate:s=()=>!0}){for(let n=1;n<=e;n++)try{return await i()}catch(r){if(n===e||!s(r,n))throw r;await new Promise(o=>setTimeout(o,t))}throw null}function oe(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,i=>{const e=Math.random()*16|0;return(i==="x"?e:e&3|8).toString(16)})}function S(i){return Array.isArray(i)?i.map(S).filter(e=>e!=null):typeof i=="object"&&i!==null?Object.entries(i).reduce((e,[t,s])=>{const n=S(s);return n!=null&&(e[t]=n),e},{}):i}function J(i,e){if("captureStackTrace"in Error)Error.captureStackTrace(i,e);else{const t=new Error;Object.defineProperty(i,"stack",{configurable:!0,get(){const{stack:s}=t;return Object.defineProperty(this,"stack",{value:s}),s}})}}async function l(i,e){i instanceof f&&J(i,e)}class f extends Error{constructor(e){super(e),this.name="Error",this.isOperational=!0,J(this,this.constructor)}}class ae extends f{constructor(e){super(`No element found for selector | ||
`)}return c});await I.default.promises.writeFile(e,a.join(` | ||
`)),console.log("\u{1F7E2} Finished"),this.currentRecord+=1}}}function Pe(i,e){const t=i[i.length-1];if(t)switch(e.type){case"keypress":{(t==null?void 0:t.type)==="keypress"&&!z(e.key)&&!z(t.key)?(i.pop(),i.push({type:"typeText",text:V(t)+V(e)})):(t==null?void 0:t.type)==="typeText"&&!z(e.key)?(i.pop(),i.push({type:"typeText",text:t.text+V(e)})):i.push(e);break}default:i.push(e)}else i.push(e)}function Y(i){var t,s,n,r,o,a;let e="";switch(i.type){case"swipe":case"tap":{const c=i.element;return typeof c=="string"?e=` on element "${c}"`:(t=c==null?void 0:c.attributes)!=null&&t.accessibilityIdentifier?e=`element with accessibilityIdentifier "${(s=c.attributes)==null?void 0:s.accessibilityIdentifier}"`:(n=c==null?void 0:c.attributes)!=null&&n.class?e=`element with class "${(r=c.attributes)==null?void 0:r.class}"`:"position"in i&&((o=i.position)==null?void 0:o.x)&&((a=i.position)==null?void 0:a.y)&&(e=`position ${Math.round(i.position.x*100)}%, ${Math.round(i.position.y*100)}%`),e?`${i.type} on ${e}`:i.type}case"keypress":return`type "${V(i)}"`;case"typeText":return`type "${i.text}"`}}function Q({type:i,value:e}){switch(i){case"deviceInfo":return{type:"deviceInfo",value:e};case"sessionRequested":return{type:"sessionRequested"};case"chromeDevToolsUrl":return{type:"networkInspectorUrl",value:e};case"orientationChanged":return{type:"orientationChanged",value:e}}}const $e="1.0.1-beta.1";class Fe extends w.EventEmitter{constructor({page:e}){super(),this.ready=!1,this.page=e,this.page.exposeFunction("__appetize_on",t=>{const s=t==="string"?t:t.type;this.emit(s,t.value),this.emit("*",{type:s,value:t.value})})}async init(){this.ready=!1,await this.page.evaluate(async([e])=>new Promise((t,s)=>{const n=setTimeout(()=>{clearInterval(r),s(new C("Timed out after 60000ms waiting for connection to Appetize page"))},6e4),r=setInterval(()=>{const o=new MessageChannel;o.port1.onmessage=()=>{clearInterval(r),clearTimeout(n),t(!1)},window.postMessage({type:"init",appetizeClient:!0,version:e},"*",[o.port2]),window.__appetize_postMessage=async(a,c=!1)=>{const h=new MessageChannel;if(window.postMessage(a,"*",[h.port2]),c)return new Promise((u,p)=>{const y=setTimeout(()=>{p(new C("Timed out after 60000ms while waiting for postMessage response"))},6e4);h.port1.onmessage=g=>{clearTimeout(y),u(g.data)}})}},100)}),[$e]),await this.page.evaluate(()=>{window.__appetize__video_frames=[],window.__appetize__audio_frames=[],window.addEventListener("message",e=>{var t;if(e.source===window){const s=typeof e.data=="string"?e.data:(t=e.data)==null?void 0:t.type;if(s==="socketEvent")switch(e.data.value.type){case"h264Data":case"frameData":if(window.__appetize__video_frames.push(e.data.value),!window.__appetize_emit_video)return;break;case"audioData":if(window.__appetize__audio_frames.push(e.data.value),!window.__appetize_emit_audio)return;break}else switch(s){case"frameData":case"recordedAction":case"playbackFoundAndSent":case"playbackNotFound":case"debug":case"interceptRequest":case"interceptResponse":case"interceptError":case"uiDump":return}window.__appetize_on(e.data)}})},[]),this.ready=!0}async waitUntilReady(){return H(async()=>{if(!this.ready)throw new C("Timed out after 60000ms while waiting for Appetize window to be ready.")},6e4)}enableVideoEvents(e=!0){return this.page.evaluate(t=>{window.__appetize_emit_video=t},e)}enableAudioEvents(e=!0){return this.page.evaluate(t=>{window.__appetize_emit_audio=t},e)}async postMessage(e,t=!1){return await this.waitUntilReady(),this.page.evaluate(async([s,n])=>window.__appetize_postMessage(s,n),[e,t])}async getVideoFrames(){return this.page.evaluate(()=>window.__appetize__video_frames)}async getAudioFrames(){return this.page.evaluate(()=>window.__appetize__audio_frames)}async resetVideoFrames(){return this.page.evaluate(()=>{window.__appetize__video_frames=[]})}async resetAudioFrames(){return this.page.evaluate(()=>{window.__appetize__audio_frames=[]})}}class X extends w.EventEmitter{constructor({page:e,type:t,window:s}){super(),this.page=e,this.type=t,this.window=s,this.window.on("*",({type:n,value:r})=>{switch(n){case"socketEvent":r.socket===this.type&&(this.emit(r.type,r.value),this.emit("*",{type:r.type,value:r.value}));break;case"disconnect":this.emit("disconnect"),this.emit("*",{type:"disconnect"});break;case"sessionInfo":case"chromeDevToolsUrl":case"orientationChanged":case"deviceInfo":if(this.type==="appetizer"){const o=Q({type:n,value:r});o&&(this.emit(o.type,o.value),this.emit("*",o))}break;case"sessionRequested":if(this.type==="webserver"){const o=Q({type:n,value:r});o&&(this.emit(o.type,o.value),this.emit("*",o))}break}})}async send(e,t){return this.window.postMessage({type:"emitSocketEvent",value:{type:e,value:t,socket:this.type}})}async disconnect(){await this.send("disconnect")}waitForEvent(e,t){return _(this,e,t)}}class Ie extends ve{constructor({page:e,config:t,window:s,testInfo:n,...r}){const o=new X({page:e,window:s,type:"appetizer"});super({...r,socket:o,config:t,logger:T}),this.actionLogs=[],this.window=s,this.page=e,this.config=t,this.testInfo=n,this.page.on("load",()=>{this.emit("disconnect")}),t.record&&this.on("disconnect",()=>{this.teardownUi()})}async addActionLog(e){if(e.error){const t=await this.getUI().catch(this.logger.warn);t&&(e.ui=t)}this.actionLogs.push(e)}teardownUi(){return this.window.page.evaluate(()=>{const e=document.querySelector("app-ui");e&&e.remove()})}async rotate(e){try{const[t]=await Promise.all([_(this.window,"orientationChanged"),await this.window.postMessage(e==="left"?"rotateLeft":"rotateRight")]);return this.window.page.waitForTimeout(1e3),t}catch(t){throw l(t,this.rotate),t}}async screenshot(e="buffer"){try{const[t]=await Promise.all([_(this.socket,"screenshot",{timeout:6e4}),this.socket.send("getScreenshot")]),s=new Uint8Array(Object.values(t.data).map(Number));return{data:e==="buffer"?Buffer.from(s):t.data,mimeType:t.mimeType}}catch(t){throw l(t,this.screenshot),t}}async record(){try{if(!this.config.record)throw new f("Recording is not enabled, please enable it by setting `record: true` in session config");if(!this.testInfo)throw new f("session.record() requires using `session` from the test() arguments");return new Te({session:this,testInfo:this.testInfo}).record()}catch(e){throw l(e,this.record),e}}async waitForEvent(e,t){try{return await _(this,e,t)}catch(s){throw l(s,this.waitForEvent),s}}async waitForTimeout(e){try{return await D(e)}catch(t){throw l(t,this.waitForTimeout),t}}async waitForElement(e,t){try{const s=await this.findElements(e,t);if(s.length){if((t==null?void 0:t.matches)&&s.length!==t.matches)throw new Error(`Expected ${t.matches} elements, found ${s.length}`);return s}else throw new Error(`Element not found: | ||
`)),console.log("\u{1F7E2} Finished"),this.currentRecord+=1}}}function Pe(i,e){const t=i[i.length-1];if(t)switch(e.type){case"keypress":{(t==null?void 0:t.type)==="keypress"&&!z(e.key)&&!z(t.key)?(i.pop(),i.push({type:"typeText",text:V(t)+V(e)})):(t==null?void 0:t.type)==="typeText"&&!z(e.key)?(i.pop(),i.push({type:"typeText",text:t.text+V(e)})):i.push(e);break}default:i.push(e)}else i.push(e)}function Y(i){var t,s,n,r,o,a;let e="";switch(i.type){case"swipe":case"tap":{const c=i.element;return typeof c=="string"?e=` on element "${c}"`:(t=c==null?void 0:c.attributes)!=null&&t.accessibilityIdentifier?e=`element with accessibilityIdentifier "${(s=c.attributes)==null?void 0:s.accessibilityIdentifier}"`:(n=c==null?void 0:c.attributes)!=null&&n.class?e=`element with class "${(r=c.attributes)==null?void 0:r.class}"`:"position"in i&&((o=i.position)==null?void 0:o.x)&&((a=i.position)==null?void 0:a.y)&&(e=`position ${Math.round(i.position.x*100)}%, ${Math.round(i.position.y*100)}%`),e?`${i.type} on ${e}`:i.type}case"keypress":return`type "${V(i)}"`;case"typeText":return`type "${i.text}"`}}function Q({type:i,value:e}){switch(i){case"deviceInfo":return{type:"deviceInfo",value:e};case"sessionRequested":return{type:"sessionRequested"};case"chromeDevToolsUrl":return{type:"networkInspectorUrl",value:e};case"orientationChanged":return{type:"orientationChanged",value:e}}}const $e="1.0.1";class Fe extends w.EventEmitter{constructor({page:e}){super(),this.ready=!1,this.page=e,this.page.exposeFunction("__appetize_on",t=>{const s=t==="string"?t:t.type;this.emit(s,t.value),this.emit("*",{type:s,value:t.value})})}async init(){this.ready=!1,await this.page.evaluate(async([e])=>new Promise((t,s)=>{const n=setTimeout(()=>{clearInterval(r),s(new C("Timed out after 60000ms waiting for connection to Appetize page"))},6e4),r=setInterval(()=>{const o=new MessageChannel;o.port1.onmessage=()=>{clearInterval(r),clearTimeout(n),t(!1)},window.postMessage({type:"init",appetizeClient:!0,version:e},"*",[o.port2]),window.__appetize_postMessage=async(a,c=!1)=>{const h=new MessageChannel;if(window.postMessage(a,"*",[h.port2]),c)return new Promise((u,p)=>{const y=setTimeout(()=>{p(new C("Timed out after 60000ms while waiting for postMessage response"))},6e4);h.port1.onmessage=g=>{clearTimeout(y),u(g.data)}})}},100)}),[$e]),await this.page.evaluate(()=>{window.__appetize__video_frames=[],window.__appetize__audio_frames=[],window.addEventListener("message",e=>{var t;if(e.source===window){const s=typeof e.data=="string"?e.data:(t=e.data)==null?void 0:t.type;if(s==="socketEvent")switch(e.data.value.type){case"h264Data":case"frameData":if(window.__appetize__video_frames.push(e.data.value),!window.__appetize_emit_video)return;break;case"audioData":if(window.__appetize__audio_frames.push(e.data.value),!window.__appetize_emit_audio)return;break}else switch(s){case"frameData":case"recordedAction":case"playbackFoundAndSent":case"playbackNotFound":case"debug":case"interceptRequest":case"interceptResponse":case"interceptError":case"uiDump":return}window.__appetize_on(e.data)}})},[]),this.ready=!0}async waitUntilReady(){return H(async()=>{if(!this.ready)throw new C("Timed out after 60000ms while waiting for Appetize window to be ready.")},6e4)}enableVideoEvents(e=!0){return this.page.evaluate(t=>{window.__appetize_emit_video=t},e)}enableAudioEvents(e=!0){return this.page.evaluate(t=>{window.__appetize_emit_audio=t},e)}async postMessage(e,t=!1){return await this.waitUntilReady(),this.page.evaluate(async([s,n])=>window.__appetize_postMessage(s,n),[e,t])}async getVideoFrames(){return this.page.evaluate(()=>window.__appetize__video_frames)}async getAudioFrames(){return this.page.evaluate(()=>window.__appetize__audio_frames)}async resetVideoFrames(){return this.page.evaluate(()=>{window.__appetize__video_frames=[]})}async resetAudioFrames(){return this.page.evaluate(()=>{window.__appetize__audio_frames=[]})}}class X extends w.EventEmitter{constructor({page:e,type:t,window:s}){super(),this.page=e,this.type=t,this.window=s,this.window.on("*",({type:n,value:r})=>{switch(n){case"socketEvent":r.socket===this.type&&(this.emit(r.type,r.value),this.emit("*",{type:r.type,value:r.value}));break;case"disconnect":this.emit("disconnect"),this.emit("*",{type:"disconnect"});break;case"sessionInfo":case"chromeDevToolsUrl":case"orientationChanged":case"deviceInfo":if(this.type==="appetizer"){const o=Q({type:n,value:r});o&&(this.emit(o.type,o.value),this.emit("*",o))}break;case"sessionRequested":if(this.type==="webserver"){const o=Q({type:n,value:r});o&&(this.emit(o.type,o.value),this.emit("*",o))}break}})}async send(e,t){return this.window.postMessage({type:"emitSocketEvent",value:{type:e,value:t,socket:this.type}})}async disconnect(){await this.send("disconnect")}waitForEvent(e,t){return _(this,e,t)}}class Ie extends ve{constructor({page:e,config:t,window:s,testInfo:n,...r}){const o=new X({page:e,window:s,type:"appetizer"});super({...r,socket:o,config:t,logger:T}),this.actionLogs=[],this.window=s,this.page=e,this.config=t,this.testInfo=n,this.page.on("load",()=>{this.emit("disconnect")}),t.record&&this.on("disconnect",()=>{this.teardownUi()})}async addActionLog(e){if(e.error){const t=await this.getUI().catch(this.logger.warn);t&&(e.ui=t)}this.actionLogs.push(e)}teardownUi(){return this.window.page.evaluate(()=>{const e=document.querySelector("app-ui");e&&e.remove()})}async rotate(e){try{const[t]=await Promise.all([_(this.window,"orientationChanged"),await this.window.postMessage(e==="left"?"rotateLeft":"rotateRight")]);return this.window.page.waitForTimeout(1e3),t}catch(t){throw l(t,this.rotate),t}}async screenshot(e="buffer"){try{const[t]=await Promise.all([_(this.socket,"screenshot",{timeout:6e4}),this.socket.send("getScreenshot")]),s=new Uint8Array(Object.values(t.data).map(Number));return{data:e==="buffer"?Buffer.from(s):t.data,mimeType:t.mimeType}}catch(t){throw l(t,this.screenshot),t}}async record(){try{if(!this.config.record)throw new f("Recording is not enabled, please enable it by setting `record: true` in session config");if(!this.testInfo)throw new f("session.record() requires using `session` from the test() arguments");return new Te({session:this,testInfo:this.testInfo}).record()}catch(e){throw l(e,this.record),e}}async waitForEvent(e,t){try{return await _(this,e,t)}catch(s){throw l(s,this.waitForEvent),s}}async waitForTimeout(e){try{return await D(e)}catch(t){throw l(t,this.waitForTimeout),t}}async waitForElement(e,t){try{const s=await this.findElements(e,t);if(s.length){if((t==null?void 0:t.matches)&&s.length!==t.matches)throw new Error(`Expected ${t.matches} elements, found ${s.length}`);return s}else throw new Error(`Element not found: | ||
${JSON.stringify(e)}`)}catch(s){throw l(s,this.waitForElement),s}}on(e,t){return e==="video"&&(q.onVideo||(this.logger.warn("Listening to the `video` event will significantly slow down your tests. It is recommended to use session.getVideoFrames() instead."),q.onVideo=!0),this.window.enableVideoEvents()),e==="audio"&&(q.onAudio||(this.logger.warn("Listening to the `audio` event will significantly slow down your tests. It is recommended to use session.getAudioFrames() instead."),q.onAudio=!0),this.window.enableAudioEvents()),super.on(e,t)}once(e,t){return e==="video"&&this.window.enableVideoEvents(),e==="audio"&&this.window.enableAudioEvents(),super.once(e,t)}async getVideoFrames(){return await this.window.getVideoFrames()}async getAudioFrames(){return await this.window.getAudioFrames()}}const q={onVideo:!1,onAudio:!1},G=new WeakMap;class Ce extends Ae{constructor({page:e}){var r;const t=(r=G.get(e))!=null?r:new Fe({page:e});G.set(e,t),t.init();const s=new X({type:"webserver",page:e,window:t});super({socket:s,window:t,logger:T}),this.window=t,this.page=e;let n=!1;this.on("queue",o=>{n||(n=!0,o.type==="concurrent"?this.logger.log(`Entered ${o.name}. Please wait until a slot becomes available.`):this.logger.log("All devices are currently in use. Please wait until requested device becomes available.")),o.position>0&&(o.type==="concurrent"?this.logger.log(`Position in ${o.name}: ${o.position}`):this.logger.log(`Position in queue: ${o.position}`))}),this.on("session",()=>{n&&(n=!1)})}validateConfig(e){var s;return{codec:((s=this.page.context().browser())==null?void 0:s.browserType().name())==="chromium"?"jpeg":"h264",...e}}createSession(e,t){return this.session=new Ie({config:e,page:this.page,window:this.window,path:t.path,token:t.token,device:this.device,app:this.app}),this.session}}class De{constructor(e){this.queue=null,this.page=e}async start(e){var r,o;const{config:t,baseURL:s}=e,n=new URLSearchParams;t.device&&n.set("device",t.device),t.deviceColor&&n.set("deviceColor",t.deviceColor),t.screenOnly&&n.set("screenOnly",t.screenOnly.toString()),t.orientation&&n.set("orientation",t.orientation),n.set("scale",(o=(r=t.scale)==null?void 0:r.toString())!=null?o:"auto"),await this.page.goto(`${s!=null?s:"https://appetize.io"}/embed/${t.publicKey}?${n.toString()}`),this.client=new Ce({page:this.page}),this.client.on("queue",a=>{this.queue=a}),await this.client.waitUntilReady(),this.session=await re(()=>this.client.startSession({...t}),{retries:5,timeout:3e4,predicate:(a,c)=>a instanceof Error&&a.message.match(/too many requests/)?(console.warn(`Too many session requests. Retrying in 30 seconds... (attempt #${c})`),!0):!1})}}let P,Z;const K=b.test.extend({_useConfig:[{},{option:!0}],config:[async({_useConfig:i},e,t)=>{await e({...t.project.use.config,...i,autoplay:!1})},{auto:!0}],_autoSnapshotSuffix:[async({},i,e)=>{e.snapshotSuffix="",await i()},{auto:!0}],appetizePage:async({browser:i,config:e},t)=>{if(P&&JSON.stringify(Z)!==JSON.stringify(e)&&(await P.page.close(),await P.page.context().close(),P=null),!P){const s=await i.newContext();if(P=new De(await s.newPage()),Z=e,!e.publicKey)throw new Error('Appetize public key not set. Make sure you have run test.use({ config: { publicKey: "..." } })')}await t(P)},session:async({appetizePage:i,config:e,baseURL:t,_doSetupAndTeardown:s},n,r)=>{if(i.session||await W.step("Start Appetize session",async()=>{await i.start({config:e,baseURL:t})}),!i.session)throw new Error("Appetize session failed to start");i.session.testInfo=r,await n(i.session)},client:async({appetizePage:i},e)=>{await e(i.client)},_doSetupAndTeardown:[async({appetizePage:i},e,t)=>{var s,n;T.clearLogHistory(),i.session&&(i.session.actionLogs=[],await i.session.window.resetVideoFrames(),await i.session.window.resetAudioFrames()),await e(),await se(i,t),t.status==="timedOut"&&((n=(s=i.queue)==null?void 0:s.position)!=null?n:0)>0&&T.warn("Test timed out while in queue for a device. You may increase your test timeout to account for higher queue times.")},{auto:!0}]}),Re=K.use,ee=K.afterEach;Object.assign(K,{setup(i){return{}.__APPETIZE__SETUP_WARNED||T.warn("test.setup() is deprecated and will be removed in a future release. Use test.use({ config: {...} }) instead"),W.use({config:i})},use(i){const{config:e,...t}=i;return Re({...t,_useConfig:e})},afterEach(i){return ee(async({appetizePage:e},t)=>{e&&await se(e,t)}),ee(i)}});const W=K,te=new WeakMap;async function se(i,e){if(te.get(e.fn))return;te.set(e.fn,!0);const t=i.session,s="fail-output";if(e.status==="failed"||e.status==="timedOut"){if(t){const o=t.actionLogs;await I.default.promises.mkdir(e.outputPath(s));const a=async()=>{try{const h=await t.screenshot("buffer");e.attach("screenshot",{body:h.data,contentType:"image/png"});const u=e.outputPath(s,"screenshot.png");await I.default.promises.writeFile(u,Buffer.from(h.data))}catch{}},c=async()=>{try{const h=o.map(y=>{if("screenshot"in y){const{screenshot:g,...v}=y;return v}return y});if(!h.length)return;const u=JSON.stringify(h,null,2);e.attach("actions",{body:u,contentType:"application/json"});const p=e.outputPath(s,"actions.json");await I.default.promises.writeFile(p,u)}catch{}};await Promise.all([a(),c()])}await(async()=>{if(!!T.logHistory.length)try{const o=`${T.logHistory.map(c=>`[${c.method}] ${JSON.stringify(c.data).slice(2,-2)}`).join(` | ||
`)}`;e.attach("sdk-logs",{body:o,contentType:"application/text"});const a=e.outputPath(s,"sdk-logs.txt");await I.default.promises.writeFile(a,o)}catch{}})()}}Object.defineProperty(d,"expect",{enumerable:!0,get:function(){return b.expect}}),d.test=W,Object.defineProperties(d,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}); | ||
//# sourceMappingURL=index.umd.js.map |
{ | ||
"name": "@appetize/playwright", | ||
"version": "1.0.1-beta.1", | ||
"version": "1.0.1", | ||
"description": "Test your mobile apps on Appetize.io with Playwright", | ||
@@ -5,0 +5,0 @@ "files": [ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
2
553362