Merge remote-tracking branch 'origin/main' into kurt/97-preview-image
This commit is contained in:
@@ -6,14 +6,15 @@
|
||||
"@netlify/functions": "^0.7.2",
|
||||
"@redwoodjs/api": "^0.34.1",
|
||||
"@sentry/node": "^6.5.1",
|
||||
"chrome-aws-lambda": "^10.1.0",
|
||||
"chrome-aws-lambda": "^9.1.0",
|
||||
"cloudinary": "^1.23.0",
|
||||
"human-id": "^2.0.1",
|
||||
"nodemailer": "^6.6.2",
|
||||
"puppeteer": "^10.1.0",
|
||||
"puppeteer-core": "^10.1.0"
|
||||
"puppeteer-core": "^9.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"puppeteer": "^9.1.1",
|
||||
"@netlify/functions": "^0.7.2",
|
||||
"@types/nodemailer": "^6.4.2"
|
||||
}
|
||||
}
|
||||
|
||||
71
app/api/src/services/og-image-generator.ts.md
Normal file
71
app/api/src/services/og-image-generator.ts.md
Normal file
@@ -0,0 +1,71 @@
|
||||
// TODO this should be in the functions folder.
|
||||
// Got the proof of concept working locally, but even though chrome-aws-lambda is supposed to fit into a AWS lambda it did not for me
|
||||
// in the mean time this is causing builds to fail so moved it out here.
|
||||
import { builder } from '@netlify/functions'
|
||||
const { headless, executablePath, puppeteer } = require('chrome-aws-lambda')
|
||||
|
||||
const captureWidth = 1200
|
||||
const captureHeight = 630
|
||||
const clipY = 0
|
||||
|
||||
async function unwrappedHandler(event, context) {
|
||||
const path = event.path
|
||||
.replace(/.+\/og-image-generator/, '')
|
||||
.replace(/\/og-image-.+\.jpg/, '')
|
||||
|
||||
const url = `${process.env.URL}/u${path}/social-card`
|
||||
|
||||
const browser = await puppeteer.launch({
|
||||
executablePath: process.env.URL?.includes('localhost')
|
||||
? null
|
||||
: await executablePath,
|
||||
args: [
|
||||
'--no-sandbox',
|
||||
'--disable-web-security',
|
||||
'--disable-gpu',
|
||||
'--hide-scrollbars',
|
||||
'--disable-setuid-sandbox',
|
||||
],
|
||||
// args: chromium.args,
|
||||
defaultViewport: {
|
||||
width: captureWidth,
|
||||
height: captureHeight + clipY,
|
||||
},
|
||||
headless: headless,
|
||||
})
|
||||
const page = await browser.newPage()
|
||||
|
||||
await page.goto(url, { waitUntil: 'networkidle0' })
|
||||
|
||||
const screenshot = await page.screenshot({
|
||||
type: 'jpeg',
|
||||
// netlify functions can only return strings, so base64 it is
|
||||
encoding: 'base64',
|
||||
quality: 70,
|
||||
clip: {
|
||||
x: 0,
|
||||
y: clipY,
|
||||
width: captureWidth,
|
||||
height: captureHeight,
|
||||
},
|
||||
})
|
||||
|
||||
await browser.close()
|
||||
|
||||
if (typeof screenshot !== 'string') {
|
||||
return {
|
||||
statusCode: 400,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
'Content-Type': 'image/jpg',
|
||||
},
|
||||
body: screenshot,
|
||||
isBase64Encoded: true,
|
||||
}
|
||||
}
|
||||
|
||||
export const handler = builder(unwrappedHandler)
|
||||
625
app/web/public/demo-worker.js
Normal file
625
app/web/public/demo-worker.js
Normal file
@@ -0,0 +1,625 @@
|
||||
(function(f) {
|
||||
if (typeof exports === "object" && typeof module !== "undefined") {
|
||||
module.exports = f()
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
define([], f)
|
||||
} else {
|
||||
var g;
|
||||
if (typeof window !== "undefined") {
|
||||
g = window
|
||||
} else if (typeof global !== "undefined") {
|
||||
g = global
|
||||
} else if (typeof self !== "undefined") {
|
||||
g = self
|
||||
} else {
|
||||
g = this
|
||||
}
|
||||
g.jscadWorker = f()
|
||||
}
|
||||
})(function() {
|
||||
// multi purpose module
|
||||
|
||||
|
||||
|
||||
const setPoints = (points, p, i)=>{
|
||||
points[i++] = p[0]
|
||||
points[i++] = p[1]
|
||||
points[i++] = p[2] || 0
|
||||
}
|
||||
|
||||
function CSG2Vertices(csg){
|
||||
let idx = 0
|
||||
|
||||
let vLen = 0, iLen = 0
|
||||
for (let poly of csg.polygons){
|
||||
let len = poly.vertices.length
|
||||
vLen += len *3
|
||||
iLen += 3 * (len-2)
|
||||
}
|
||||
const vertices = new Float32Array(vLen)
|
||||
const indices = vLen > 65535 ? new Uint32Array(iLen) : new Uint16Array(iLen)
|
||||
|
||||
let vertOffset = 0
|
||||
let indOffset = 0
|
||||
let posOffset = 0
|
||||
let first = 0
|
||||
for (let poly of csg.polygons){
|
||||
let arr = poly.vertices
|
||||
let len = arr.length
|
||||
first = posOffset
|
||||
vertices.set(arr[0], vertOffset)
|
||||
vertOffset +=3
|
||||
vertices.set(arr[1], vertOffset)
|
||||
vertOffset +=3
|
||||
posOffset +=2
|
||||
for(let i=2; i<len; i++){
|
||||
vertices.set(arr[i], vertOffset)
|
||||
|
||||
indices[indOffset++] = first
|
||||
indices[indOffset++] = first + i -1
|
||||
indices[indOffset++] = first + i
|
||||
|
||||
vertOffset += 3
|
||||
posOffset += 1
|
||||
}
|
||||
}
|
||||
return {vertices, indices, type:'mesh'}
|
||||
}
|
||||
|
||||
|
||||
function CSG2LineVertices(csg){
|
||||
let vLen = csg.points.length * 3
|
||||
if(csg.isClosed) vLen += 3
|
||||
|
||||
var vertices = new Float32Array(vLen)
|
||||
|
||||
|
||||
csg.points.forEach((p,idx)=>setPoints(vertices, p, idx * 3 ))
|
||||
|
||||
if(csg.isClosed){
|
||||
setPoints(vertices, csg.points[0], vertices.length - 3 )
|
||||
}
|
||||
return {vertices, type:'line'}
|
||||
}
|
||||
|
||||
function CSG2LineSegmentsVertices(csg){
|
||||
let vLen = csg.sides.length * 6
|
||||
|
||||
var vertices = new Float32Array(vLen)
|
||||
csg.sides.forEach((side,idx)=>{
|
||||
let i = idx * 6
|
||||
setPoints(vertices, side[0], i)
|
||||
setPoints(vertices, side[1], i+3)
|
||||
})
|
||||
return {vertices, type:'lines'}
|
||||
|
||||
}
|
||||
|
||||
function CSGCached(func, data, cacheKey, transferable){
|
||||
cacheKey = cacheKey || data
|
||||
|
||||
let geo = CSGToBuffers.cache.get(cacheKey)
|
||||
if(geo) return geo
|
||||
|
||||
geo = func(data)
|
||||
|
||||
// fill transferable array for postMessage optimization
|
||||
if(transferable){
|
||||
const {vertices, indices} = geo
|
||||
transferable.push(vertices)
|
||||
if(indices) transferable.push(indices)
|
||||
}
|
||||
|
||||
CSGToBuffers.cache.set(cacheKey, geo)
|
||||
return geo
|
||||
}
|
||||
|
||||
function CSGToBuffers(csg, transferable){
|
||||
let obj
|
||||
|
||||
if(csg.polygons) obj = CSGCached(CSG2Vertices,csg,csg.polygons, transferable)
|
||||
if(csg.sides && !csg.points) obj = CSGCached(CSG2LineSegmentsVertices,csg,csg.sides, transferable)
|
||||
if(csg.points) obj = CSGCached(CSG2LineVertices,csg,csg.points, transferable)
|
||||
|
||||
return obj
|
||||
}
|
||||
CSGToBuffers.clearCache = ()=>{CSGToBuffers.cache = new WeakMap()}
|
||||
CSGToBuffers.clearCache()
|
||||
|
||||
|
||||
|
||||
|
||||
let workerBaseURI
|
||||
|
||||
function require(url){
|
||||
url = require.alias[url] || url
|
||||
if(url[0] != '/' && url.substr(0,2) != './' && url.substr(0,4) != 'http') url = 'https://unpkg.com/'+url
|
||||
let exports=require.cache[url]; //get from cache
|
||||
if (!exports) { //not cached
|
||||
let module = requireModule(url)
|
||||
require.cache[url] = exports = module.exports; //cache obj exported by module
|
||||
}
|
||||
return exports; //require returns object exported by module
|
||||
}
|
||||
|
||||
function requireFile(url){
|
||||
try{
|
||||
let X=new XMLHttpRequest();
|
||||
X.open("GET", new URL(url,workerBaseURI), 0); // sync
|
||||
X.send();
|
||||
if (X.status && X.status !== 200) throw new Error(X.statusText);
|
||||
return X.responseText;
|
||||
}catch(e){
|
||||
console.log('problem loading url ',url,'base',workerBaseURI,' error:',e.message)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
function requireModule(url, source){
|
||||
try {
|
||||
const exports={};
|
||||
if(!source) source = requireFile(url)
|
||||
const module = { id: url, uri: url, exports:exports, source }; //according to node.js modules
|
||||
// fix, add comment to show source on Chrome Dev Tools
|
||||
source="//@ sourceURL="+url+"\n" + source;
|
||||
//------
|
||||
const anonFn = new Function("require", "exports", "module", source); //create a Fn with module code, and 3 params: require, exports & module
|
||||
anonFn(require, exports, module); // call the Fn, Execute the module
|
||||
return module
|
||||
} catch (err) {
|
||||
console.error("Error loading module "+url, err.message);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
require.cache = {}
|
||||
require.alias = {}
|
||||
|
||||
|
||||
const initCanvas = (canvas, callback)=>{
|
||||
|
||||
// convert HTML events (mouse movement) to viewer changes
|
||||
let lastX = 0
|
||||
let lastY = 0
|
||||
|
||||
let pointerDown = false
|
||||
|
||||
const moveHandler = (ev) => {
|
||||
if(!pointerDown) return
|
||||
const cmd = {
|
||||
worker: 'render',
|
||||
dx: lastX - ev.pageX,
|
||||
dy: ev.pageY - lastY
|
||||
}
|
||||
|
||||
const shiftKey = (ev.shiftKey === true) || (ev.touches && ev.touches.length > 2)
|
||||
cmd.action = shiftKey ? 'pan':'rotate'
|
||||
callback(cmd)
|
||||
|
||||
lastX = ev.pageX
|
||||
lastY = ev.pageY
|
||||
|
||||
ev.preventDefault()
|
||||
}
|
||||
const downHandler = (ev) => {
|
||||
pointerDown = true
|
||||
lastX = ev.pageX
|
||||
lastY = ev.pageY
|
||||
canvas.setPointerCapture(ev.pointerId)
|
||||
ev.preventDefault()
|
||||
}
|
||||
|
||||
const upHandler = (ev) => {
|
||||
pointerDown = false
|
||||
canvas.releasePointerCapture(ev.pointerId)
|
||||
ev.preventDefault()
|
||||
}
|
||||
|
||||
const wheelHandler = (ev) => {
|
||||
callback({action:'zoom', dy:ev.deltaY, worker: 'render'})
|
||||
ev.preventDefault()
|
||||
}
|
||||
|
||||
canvas.onpointermove = moveHandler
|
||||
canvas.onpointerdown = downHandler
|
||||
canvas.onpointerup = upHandler
|
||||
canvas.onwheel = wheelHandler
|
||||
}
|
||||
|
||||
const cmdHandler = (handlers)=>(cmd)=>{
|
||||
const fn = handlers[cmd.action]
|
||||
if (!fn) throw new Error('no handler for type: ' + cmd.action)
|
||||
fn(cmd);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const makeScriptWorker = ({callback, convertToSolids})=>{
|
||||
let workerBaseURI, onInit
|
||||
|
||||
function runMain(params={}){
|
||||
let time = Date.now()
|
||||
let solids
|
||||
try{
|
||||
solids = main(params)
|
||||
}catch(e){
|
||||
callback({action:'entities', worker:'render', error:e.message, stack:e.stack.toString()}, transfer)
|
||||
return
|
||||
}
|
||||
let solidsTime = Date.now() - time
|
||||
scriptStats = `generate solids ${solidsTime}ms`
|
||||
|
||||
let transfer = []
|
||||
if(convertToSolids === 'buffers'){
|
||||
CSGToBuffers.clearCache()
|
||||
entities = solids.map((csg)=>{
|
||||
let obj = CSGToBuffers(csg, transfer)
|
||||
obj.color = csg.color
|
||||
obj.transforms = csg.transforms
|
||||
return obj
|
||||
})
|
||||
}else if(convertToSolids === 'regl'){
|
||||
const { entitiesFromSolids } = require('@jscad/regl-renderer')
|
||||
time = Date.now()
|
||||
entities = entitiesFromSolids({}, solids)
|
||||
scriptStats += ` convert to entities ${Date.now()-time}ms`
|
||||
}else{
|
||||
entities = solids
|
||||
}
|
||||
callback({action:'entities', worker:'render', entities, scriptStats}, transfer)
|
||||
}
|
||||
|
||||
let initialized = false
|
||||
const handlers = {
|
||||
runScript: ({script,url, params={}})=>{
|
||||
if(!initialized){
|
||||
onInit = ()=>handlers.runScript({script,url, params})
|
||||
}
|
||||
let script_module
|
||||
try{
|
||||
script_module = requireModule(url,script)
|
||||
}catch(e){
|
||||
callback({action:'entities', worker:'render', error:e.message, stack:e.stack.toString()})
|
||||
return
|
||||
}
|
||||
main = script_module.exports.main
|
||||
let gp = script_module.exports.getParameterDefinitions
|
||||
if(gp){
|
||||
callback({action:'parameterDefinitions', worker:'main', data:gp()})
|
||||
}
|
||||
runMain(params)
|
||||
},
|
||||
updateParams: ({params={}})=>{
|
||||
runMain(params)
|
||||
},
|
||||
init: (params)=>{
|
||||
let {baseURI, alias=[]} = params
|
||||
if(!baseURI && typeof document != 'undefined' && document.baseURI){
|
||||
baseURI = document.baseURI
|
||||
}
|
||||
|
||||
if(baseURI) workerBaseURI = baseURI.toString()
|
||||
|
||||
alias.forEach(arr=>{
|
||||
let [orig, ...aliases] = arr
|
||||
aliases.forEach(a=>{
|
||||
require.alias[a] = orig
|
||||
if(a.toLowerCase().substr(-3)!=='.js') require.alias[a+'.js'] = orig
|
||||
})
|
||||
})
|
||||
initialized = true
|
||||
if(onInit) onInit()
|
||||
},
|
||||
}
|
||||
|
||||
return {
|
||||
// called from outside to pass mesasges into worker
|
||||
postMessage: cmdHandler(handlers),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Make render worker */
|
||||
|
||||
const makeRenderWorker = ()=>{
|
||||
let perspectiveCamera
|
||||
const state = {}
|
||||
|
||||
const rotateSpeed = 0.002
|
||||
const panSpeed = 1
|
||||
const zoomSpeed = 0.08
|
||||
let rotateDelta = [0, 0]
|
||||
let panDelta = [0, 0]
|
||||
let zoomDelta = 0
|
||||
let updateRender = true
|
||||
let orbitControls, renderOptions, gridOptions, axisOptions, renderer
|
||||
|
||||
let entities = []
|
||||
|
||||
function createContext (canvas, contextAttributes) {
|
||||
function get (type) {
|
||||
try {
|
||||
return {gl:canvas.getContext(type, contextAttributes), type}
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
return (
|
||||
get('webgl2') ||
|
||||
get('webgl') ||
|
||||
get('experimental-webgl') ||
|
||||
get('webgl-experimental')
|
||||
)
|
||||
}
|
||||
|
||||
const startRenderer = ({canvas, cameraPosition, cameraTarget, axis={}, grid={}})=>{
|
||||
const { prepareRender, drawCommands, cameras, controls } = require('@jscad/regl-renderer')
|
||||
|
||||
perspectiveCamera = cameras.perspective
|
||||
orbitControls = controls.orbit
|
||||
|
||||
state.canvas = canvas
|
||||
state.camera = Object.assign({}, perspectiveCamera.defaults)
|
||||
if(cameraPosition) state.camera.position = cameraPosition
|
||||
if(cameraTarget) state.camera.target = cameraTarget
|
||||
|
||||
resize({ width:canvas.width, height:canvas.height })
|
||||
|
||||
state.controls = orbitControls.defaults
|
||||
|
||||
const {gl, type} = createContext(canvas)
|
||||
// prepare the renderer
|
||||
const setupOptions = {
|
||||
glOptions: {gl}
|
||||
}
|
||||
if(type == 'webgl'){
|
||||
setupOptions.glOptions.optionalExtensions = ['oes_element_index_uint']
|
||||
}
|
||||
renderer = prepareRender(setupOptions)
|
||||
|
||||
gridOptions = {
|
||||
visuals: {
|
||||
drawCmd: 'drawGrid',
|
||||
show: grid.show || grid.show === undefined ,
|
||||
color: grid.color || [0, 0, 0, 1],
|
||||
subColor: grid.subColor || [0, 0, 1, 0.5],
|
||||
fadeOut: false,
|
||||
transparent: true
|
||||
},
|
||||
size: grid.size || [200, 200],
|
||||
ticks: grid.ticks || [10, 1]
|
||||
}
|
||||
|
||||
axisOptions = {
|
||||
visuals: {
|
||||
drawCmd: 'drawAxis',
|
||||
show: axis.show || axis.show === undefined
|
||||
},
|
||||
size: axis.size || 100,
|
||||
}
|
||||
|
||||
// assemble the options for rendering
|
||||
renderOptions = {
|
||||
camera: state.camera,
|
||||
drawCommands: {
|
||||
drawAxis: drawCommands.drawAxis,
|
||||
drawGrid: drawCommands.drawGrid,
|
||||
drawLines: drawCommands.drawLines,
|
||||
drawMesh: drawCommands.drawMesh
|
||||
},
|
||||
// define the visual content
|
||||
entities: [
|
||||
gridOptions,
|
||||
axisOptions,
|
||||
...entities
|
||||
]
|
||||
}
|
||||
// the heart of rendering, as themes, controls, etc change
|
||||
|
||||
updateView()
|
||||
}
|
||||
|
||||
let renderTimer
|
||||
const tmFunc = typeof requestAnimationFrame === 'undefined' ? setTimeout : requestAnimationFrame
|
||||
|
||||
function updateView(delay=8){
|
||||
if(renderTimer || !renderer) return
|
||||
renderTimer = tmFunc(updateAndRender,delay)
|
||||
}
|
||||
|
||||
const doRotatePanZoom = () => {
|
||||
|
||||
if (rotateDelta[0] || rotateDelta[1]) {
|
||||
const updated = orbitControls.rotate({ controls: state.controls, camera: state.camera, speed: rotateSpeed }, rotateDelta)
|
||||
state.controls = { ...state.controls, ...updated.controls }
|
||||
rotateDelta = [0, 0]
|
||||
}
|
||||
|
||||
if (panDelta[0] || panDelta[1]) {
|
||||
const updated = orbitControls.pan({ controls:state.controls, camera:state.camera, speed: panSpeed }, panDelta)
|
||||
state.controls = { ...state.controls, ...updated.controls }
|
||||
panDelta = [0, 0]
|
||||
state.camera.position = updated.camera.position
|
||||
state.camera.target = updated.camera.target
|
||||
}
|
||||
|
||||
if (zoomDelta) {
|
||||
const updated = orbitControls.zoom({ controls:state.controls, camera:state.camera, speed: zoomSpeed }, zoomDelta)
|
||||
state.controls = { ...state.controls, ...updated.controls }
|
||||
zoomDelta = 0
|
||||
}
|
||||
}
|
||||
|
||||
const updateAndRender = (timestamp) => {
|
||||
renderTimer = null
|
||||
doRotatePanZoom()
|
||||
|
||||
const updates = orbitControls.update({ controls: state.controls, camera: state.camera })
|
||||
state.controls = { ...state.controls, ...updates.controls }
|
||||
if(state.controls.changed) updateView(16) // for elasticity in rotate / zoom
|
||||
|
||||
state.camera.position = updates.camera.position
|
||||
perspectiveCamera.update(state.camera)
|
||||
renderOptions.entities = [
|
||||
gridOptions,
|
||||
axisOptions,
|
||||
...entities
|
||||
]
|
||||
let time = Date.now()
|
||||
renderer(renderOptions)
|
||||
if(updateRender){
|
||||
console.log(updateRender, ' first render', Date.now()-time);
|
||||
updateRender = '';
|
||||
}
|
||||
}
|
||||
|
||||
function resize({width,height}){
|
||||
state.canvas.width = width
|
||||
state.canvas.height = height
|
||||
perspectiveCamera.setProjection(state.camera, state.camera, { width, height })
|
||||
perspectiveCamera.update(state.camera, state.camera)
|
||||
updateView()
|
||||
}
|
||||
|
||||
const handlers = {
|
||||
pan: ({dx,dy})=>{
|
||||
panDelta[0] += dx
|
||||
panDelta[1] += dy
|
||||
updateView()
|
||||
},
|
||||
rotate: ({dx,dy})=>{
|
||||
rotateDelta[0] -= dx
|
||||
rotateDelta[1] -= dy
|
||||
updateView()
|
||||
},
|
||||
zoom: ({dy})=>{
|
||||
zoomDelta += dy
|
||||
updateView()
|
||||
},
|
||||
resize,
|
||||
entities: (params)=>{
|
||||
entities = params.entities
|
||||
updateRender = params.scriptStats
|
||||
updateView()
|
||||
},
|
||||
init: (params)=>{
|
||||
if(params.canvas) startRenderer(params)
|
||||
initialized = true
|
||||
},
|
||||
}
|
||||
|
||||
return {
|
||||
// called from outside to pass mesasges into worker
|
||||
postMessage: cmdHandler(handlers),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return (params)=>{
|
||||
let { canvas, baseURI=(typeof document === 'undefined') ? '':document.location.toString(), scope='main', renderInWorker, render, callback=()=>{}, scriptUrl='demo-worker.js', alias, convertToSolids=false } = params
|
||||
// by default 'render' messages go outside of this instance (result of modeling)
|
||||
let sendToRender = callback
|
||||
let scriptWorker, renderWorker
|
||||
workerBaseURI = baseURI
|
||||
|
||||
const sendCmd = (params, transfer)=>{
|
||||
if(params.worker === 'render')
|
||||
sendToRender(params, transfer)
|
||||
else if(params.worker === 'script')
|
||||
scriptWorker.postMessage(params, transfer)
|
||||
else{
|
||||
// parameter definitions will arrive from scriptWorker
|
||||
callback(params, transfer)
|
||||
}
|
||||
}
|
||||
|
||||
const updateSize = function({width,height}){
|
||||
sendCmd({ action:'resize', worker:'render', width: canvas.offsetWidth, height: canvas.offsetHeight})
|
||||
}
|
||||
|
||||
|
||||
renderInWorker = !!(canvas && renderInWorker && canvas.transferControlToOffscreen)
|
||||
const makeRenderWorkerHere = (scope === 'main' && canvas && !renderInWorker) || (scope === 'worker' && render)
|
||||
// worker is in current thread
|
||||
if(makeRenderWorkerHere){
|
||||
console.log('render in scope: '+scope);
|
||||
renderWorker = makeRenderWorker({callback:sendCmd})
|
||||
sendToRender = (params, transfer)=>renderWorker.postMessage(params, transfer)
|
||||
}
|
||||
|
||||
if(scope === 'main'){
|
||||
// let extraScript = renderInWorker ? `,'https://unpkg.com/@jscad/regl-renderer'`:''
|
||||
let script =`let baseURI = '${baseURI}'
|
||||
importScripts(new URL('${scriptUrl}',baseURI))
|
||||
let worker = jscadWorker({
|
||||
baseURI: baseURI,
|
||||
convertToSolids: ${convertToSolids},
|
||||
scope:'worker',
|
||||
callback:(params)=>self.postMessage(params),
|
||||
render:${renderInWorker}
|
||||
})
|
||||
self.addEventListener('message', (e)=>worker.postMessage(e.data))
|
||||
`
|
||||
let blob = new Blob([script],{type: 'text/javascript'})
|
||||
scriptWorker = new Worker(window.URL.createObjectURL(blob))
|
||||
scriptWorker.addEventListener('message',(e)=>sendCmd(e.data))
|
||||
scriptWorker.postMessage({action:'init', baseURI, alias})
|
||||
if(renderInWorker) renderWorker = scriptWorker
|
||||
|
||||
if(canvas){
|
||||
initCanvas(canvas, sendCmd)
|
||||
window.addEventListener('resize',updateSize)
|
||||
}
|
||||
}else{
|
||||
scriptWorker = makeScriptWorker({callback:sendCmd, convertToSolids})
|
||||
callback({action:'workerInit',worker:'main'})
|
||||
}
|
||||
|
||||
if(canvas){
|
||||
// redirect 'render' messages to renderWorker
|
||||
sendToRender = (params, transfer)=>renderWorker.postMessage(params, transfer)
|
||||
let width = canvas.width = canvas.clientWidth
|
||||
let height = canvas.height = canvas.clientHeight
|
||||
if(scope == 'main'){
|
||||
const offscreen = renderInWorker ? canvas.transferControlToOffscreen() : canvas
|
||||
renderWorker.postMessage({action:'init', worker:'render', canvas:offscreen, width, height}, [offscreen])
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
updateSize,
|
||||
updateParams:({params={}})=>sendCmd({ action:'updateParams', worker:'script', params}),
|
||||
runScript: ({script,url=''})=>sendCmd({ action:'runScript', worker:'script', script, url}),
|
||||
postMessage: sendCmd,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// multi purpose module
|
||||
});
|
||||
@@ -35,17 +35,19 @@ export const makeStlDownloadHandler =
|
||||
ideType,
|
||||
}: makeStlDownloadHandlerArgs) =>
|
||||
() => {
|
||||
const makeStlBlobFromGeo = flow(
|
||||
(geo) => new Mesh(geo, new MeshBasicMaterial()),
|
||||
(mesh) => new Scene().add(mesh),
|
||||
const makeStlBlobFromMesh = flow(
|
||||
(...meshes) => new Scene().add(...meshes),
|
||||
(scene) => new STLExporter().parse(scene),
|
||||
(stl) =>
|
||||
new Blob([stl], {
|
||||
type: 'text/plain',
|
||||
})
|
||||
)
|
||||
const saveFile = (geometry) => {
|
||||
const blob = makeStlBlobFromGeo(geometry)
|
||||
const makeStlBlobFromGeo = flow(
|
||||
(geo) => new Mesh(geo, new MeshBasicMaterial()),
|
||||
(mesh) => makeStlBlobFromMesh(mesh)
|
||||
)
|
||||
const saveFile = (blob) => {
|
||||
fileSave(blob, {
|
||||
fileName,
|
||||
extensions: ['.stl'],
|
||||
@@ -56,7 +58,9 @@ export const makeStlDownloadHandler =
|
||||
type === 'geometry' &&
|
||||
(quality === 'high' || ideType === 'openscad')
|
||||
) {
|
||||
saveFile(geometry)
|
||||
saveFile(makeStlBlobFromGeo(geometry))
|
||||
} else if (ideType == 'jscad') {
|
||||
saveFile(makeStlBlobFromMesh(...geometry))
|
||||
} else {
|
||||
thunkDispatch((dispatch, getState) => {
|
||||
const state = getState()
|
||||
|
||||
@@ -21,6 +21,12 @@ function Asset({ geometry: incomingGeo }) {
|
||||
}
|
||||
}, [incomingGeo])
|
||||
if (!incomingGeo) return null
|
||||
|
||||
if (incomingGeo.length)
|
||||
return incomingGeo.map((shape, index) => (
|
||||
<primitive object={shape} key={index} />
|
||||
))
|
||||
|
||||
return (
|
||||
<mesh ref={mesh} scale={[1, 1, 1]}>
|
||||
<bufferGeometry attach="geometry" ref={ref} />
|
||||
@@ -209,8 +215,8 @@ const IdeViewer = ({ Loading }) => {
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<ambientLight intensity={1} />
|
||||
<pointLight position={[15, 5, 10]} intensity={4} />
|
||||
<ambientLight intensity={0.3} />
|
||||
<pointLight position={[15, 5, 10]} intensity={0.1} />
|
||||
<pointLight position={[-1000, -1000, -1000]} intensity={1} />
|
||||
<pointLight position={[-1000, 0, 1000]} intensity={1} />
|
||||
{state.objectData?.type === 'png' && (
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
import { Helmet } from 'react-helmet'
|
||||
|
||||
const Seo = ({ title, description, lang, socialImageUrl}: { title: string; description: string; lang: string; socialImageUrl?: string}) => {
|
||||
const Seo = ({
|
||||
title,
|
||||
description,
|
||||
lang,
|
||||
socialImageUrl,
|
||||
}: {
|
||||
title: string
|
||||
description: string
|
||||
lang: string
|
||||
socialImageUrl?: string
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<Helmet
|
||||
|
||||
@@ -41,7 +41,10 @@ export const Success = ({
|
||||
const image = userProject?.Project?.mainImage
|
||||
const gravatar = userProject?.image
|
||||
return (
|
||||
<div className="flex-col flex h-screen bg-ch-gray-800 text-ch-gray-300" id="social-card-loaded">
|
||||
<div
|
||||
className="flex-col flex h-screen bg-ch-gray-800 text-ch-gray-300"
|
||||
id="social-card-loaded"
|
||||
>
|
||||
<div
|
||||
className="flex-grow grid"
|
||||
style={{ gridTemplateColumns: '7fr 5fr' }}
|
||||
|
||||
@@ -5,7 +5,7 @@ import openscad from './openScadController'
|
||||
import cadquery from './cadQueryController'
|
||||
import jscad from './jsCadController'
|
||||
|
||||
export const cadPackages: {[key in CadPackage]: DefaultKernelExport} = {
|
||||
export const cadPackages: { [key in CadPackage]: DefaultKernelExport } = {
|
||||
openscad,
|
||||
cadquery,
|
||||
jscad,
|
||||
|
||||
@@ -1,11 +1,137 @@
|
||||
import { RenderArgs, DefaultKernelExport, createUnhealthyResponse } from './common'
|
||||
import {
|
||||
RenderArgs,
|
||||
DefaultKernelExport,
|
||||
createUnhealthyResponse,
|
||||
createHealthyResponse,
|
||||
} from './common'
|
||||
import {
|
||||
MeshPhongMaterial,
|
||||
LineBasicMaterial,
|
||||
BufferGeometry,
|
||||
BufferAttribute,
|
||||
Line,
|
||||
LineSegments,
|
||||
Color,
|
||||
Mesh,
|
||||
} from 'three'
|
||||
|
||||
const materials = {
|
||||
mesh: {
|
||||
def: new MeshPhongMaterial({ color: 0x0084d1, flatShading: true }),
|
||||
material: (params) => new MeshPhongMaterial(params),
|
||||
},
|
||||
line: {
|
||||
def: new LineBasicMaterial({ color: 0x0000ff }),
|
||||
material: ({ color, opacity, transparent }) =>
|
||||
new LineBasicMaterial({ color, opacity, transparent }),
|
||||
},
|
||||
lines: null,
|
||||
}
|
||||
materials.lines = materials.line
|
||||
|
||||
function CSG2Object3D(obj) {
|
||||
const { vertices, indices, color, transforms } = obj
|
||||
|
||||
const materialDef = materials[obj.type]
|
||||
if (!materialDef) {
|
||||
console.error('Can not hangle object type: ' + obj.type, obj)
|
||||
return null
|
||||
}
|
||||
|
||||
let material = materialDef.def
|
||||
if (color) {
|
||||
const c = color
|
||||
material = materialDef.material({
|
||||
color: new Color(c[0], c[1], c[2]),
|
||||
flatShading: true,
|
||||
opacity: c[3] === void 0 ? 1 : c[3],
|
||||
transparent: c[3] != 1 && c[3] !== void 0,
|
||||
})
|
||||
}
|
||||
|
||||
const geo = new BufferGeometry()
|
||||
geo.setAttribute('position', new BufferAttribute(vertices, 3))
|
||||
|
||||
let mesh
|
||||
switch (obj.type) {
|
||||
case 'mesh':
|
||||
geo.setIndex(new BufferAttribute(indices, 1))
|
||||
mesh = new Mesh(geo, material)
|
||||
break
|
||||
case 'line':
|
||||
mesh = new Line(geo, material)
|
||||
break
|
||||
case 'lines':
|
||||
mesh = new LineSegments(geo, material)
|
||||
break
|
||||
}
|
||||
if (transforms) mesh.applyMatrix4({ elements: transforms })
|
||||
return mesh
|
||||
}
|
||||
|
||||
let scriptWorker
|
||||
const scriptUrl = '/demo-worker.js'
|
||||
let resolveReference = null
|
||||
let response = null
|
||||
|
||||
const callResolve = () => {
|
||||
if (resolveReference) resolveReference()
|
||||
resolveReference
|
||||
}
|
||||
|
||||
export const render: DefaultKernelExport['render'] = async ({
|
||||
code,
|
||||
settings,
|
||||
}: RenderArgs) => {
|
||||
// do your magic
|
||||
return createUnhealthyResponse( new Date(), 'JSCAD controller not implemented yet')
|
||||
if (!scriptWorker) {
|
||||
const baseURI = document.baseURI.toString()
|
||||
const script = `let baseURI = '${baseURI}'
|
||||
importScripts(new URL('${scriptUrl}',baseURI))
|
||||
let worker = jscadWorker({
|
||||
baseURI: baseURI,
|
||||
scope:'worker',
|
||||
convertToSolids: 'buffers',
|
||||
callback:(params)=>self.postMessage(params),
|
||||
})
|
||||
self.addEventListener('message', (e)=>worker.postMessage(e.data))
|
||||
`
|
||||
const blob = new Blob([script], { type: 'text/javascript' })
|
||||
scriptWorker = new Worker(window.URL.createObjectURL(blob))
|
||||
scriptWorker.addEventListener('message', (e) => {
|
||||
const data = e.data
|
||||
if (data.action == 'entities') {
|
||||
if (data.error) {
|
||||
response = createUnhealthyResponse(new Date(), data.error)
|
||||
} else {
|
||||
response = createHealthyResponse({
|
||||
type: 'geometry',
|
||||
data: [...data.entities.map(CSG2Object3D).filter((o) => o)],
|
||||
consoleMessage: data.scriptStats,
|
||||
date: new Date(),
|
||||
})
|
||||
}
|
||||
callResolve()
|
||||
}
|
||||
})
|
||||
|
||||
callResolve()
|
||||
response = null
|
||||
scriptWorker.postMessage({ action: 'init', baseURI, alias: [] })
|
||||
}
|
||||
scriptWorker.postMessage({
|
||||
action: 'runScript',
|
||||
worker: 'script',
|
||||
script: code,
|
||||
url: 'jscad_script',
|
||||
})
|
||||
|
||||
const waitResult = new Promise((resolve) => {
|
||||
resolveReference = resolve
|
||||
})
|
||||
|
||||
await waitResult
|
||||
resolveReference = null
|
||||
return response
|
||||
}
|
||||
|
||||
const jsCadController: DefaultKernelExport = {
|
||||
|
||||
@@ -11,7 +11,7 @@ function withThunk(dispatch, getState) {
|
||||
|
||||
export type CadPackage = 'openscad' | 'cadquery' | 'jscad'
|
||||
|
||||
const initCodeMap: {[key in CadPackage]: string} = {
|
||||
const initCodeMap: { [key in CadPackage]: string } = {
|
||||
openscad: `// involute donut
|
||||
|
||||
// ^ first comment is used for download title (i.e "involute-donut.stl")
|
||||
@@ -42,9 +42,33 @@ result = (cq.Workplane().circle(diam).extrude(20.0)
|
||||
|
||||
show_object(result)
|
||||
`,
|
||||
jscad: `
|
||||
// TODO implement example JSCAD code.
|
||||
`
|
||||
jscad: `
|
||||
const { booleans, colors, primitives } = require('@jscad/modeling') // modeling comes from the included MODELING library
|
||||
|
||||
const { intersect, subtract } = booleans
|
||||
const { colorize } = colors
|
||||
const { cube, cuboid, line, sphere, star } = primitives
|
||||
|
||||
const main = ({scale=1}) => {
|
||||
const logo = [
|
||||
colorize([1.0, 0.4, 1.0], subtract(
|
||||
cube({ size: 300 }),
|
||||
sphere({ radius: 200 })
|
||||
)),
|
||||
colorize([1.0, 1.0, 0], intersect(
|
||||
sphere({ radius: 130 }),
|
||||
cube({ size: 210 })
|
||||
))
|
||||
]
|
||||
|
||||
const transpCube = colorize([1, 0, 0, 0.75], cuboid({ size: [100 * scale, 100, 210 + (200 * scale)] }))
|
||||
const star2D = star({ vertices: 8, innerRadius: 150, outerRadius: 200 })
|
||||
const line2D = colorize([1.0, 0, 0], line([[220, 220], [-220, 220], [-220, -220], [220, -220], [220, 220]]))
|
||||
|
||||
return [transpCube, star2D, line2D, ...logo]
|
||||
}
|
||||
module.exports = {main}
|
||||
`,
|
||||
}
|
||||
|
||||
const codeStorageKey = 'Last-editor-code'
|
||||
|
||||
@@ -9,11 +9,20 @@ import { Toaster } from '@redwoodjs/web/toast'
|
||||
const ProjectPage = ({ userName, projectTitle }) => {
|
||||
const { currentUser } = useAuth()
|
||||
const [state, thunkDispatch] = useIdeState()
|
||||
const cacheInvalidator = new Date().toISOString().split('-').slice(0, 2).join('-')
|
||||
const cacheInvalidator = new Date()
|
||||
.toISOString()
|
||||
.split('-')
|
||||
.slice(0, 2)
|
||||
.join('-')
|
||||
const socialImageUrl = `/.netlify/functions/og-image-generator/${userName}/${projectTitle}/og-image-${cacheInvalidator}.jpg`
|
||||
return (
|
||||
<>
|
||||
<Seo title={projectTitle} description={projectTitle} socialImageUrl={socialImageUrl} lang="en-US" />
|
||||
<Seo
|
||||
title={projectTitle}
|
||||
description={projectTitle}
|
||||
socialImageUrl={socialImageUrl}
|
||||
lang="en-US"
|
||||
/>
|
||||
<Toaster timeout={1500} />
|
||||
<IdeContext.Provider value={{ state, thunkDispatch, project: null }}>
|
||||
<ProjectCell
|
||||
|
||||
141
app/yarn.lock
141
app/yarn.lock
@@ -6483,10 +6483,10 @@ chownr@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
|
||||
integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
|
||||
|
||||
chrome-aws-lambda@^10.1.0:
|
||||
version "10.1.0"
|
||||
resolved "https://registry.yarnpkg.com/chrome-aws-lambda/-/chrome-aws-lambda-10.1.0.tgz#ac43b4cdfc1fbb2275c62effada560858099501e"
|
||||
integrity sha512-NZQVf+J4kqG4sVhRm3WNmOfzY0OtTSm+S8rg77pwePa9RCYHzhnzRs8YvNI6L9tALIW6RpmefWiPURt3vURXcw==
|
||||
chrome-aws-lambda@^9.1.0:
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/chrome-aws-lambda/-/chrome-aws-lambda-9.1.0.tgz#f3a426fa6588f2fa9452ff62d60afaa2103b2c04"
|
||||
integrity sha512-y6MOarSzL5LmDv8sWUdDe4wHuAAEeWMlCbLwmL6Bj+pBPava37UuEPbWD5YAXyJx6oHoC0efLGqYDi56k4KMUQ==
|
||||
dependencies:
|
||||
lambdafs "^2.0.3"
|
||||
|
||||
@@ -7578,7 +7578,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@4, debug@4.3.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.0, debug@^4.3.1:
|
||||
debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.0, debug@^4.3.1:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
|
||||
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
|
||||
@@ -7851,10 +7851,10 @@ detective@^5.2.0:
|
||||
defined "^1.0.0"
|
||||
minimist "^1.1.1"
|
||||
|
||||
devtools-protocol@0.0.883894:
|
||||
version "0.0.883894"
|
||||
resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.883894.tgz#d403f2c75cd6d71c916aee8dde9258da988a4da9"
|
||||
integrity sha512-33idhm54QJzf3Q7QofMgCvIVSd2o9H3kQPWaKT/fhoZh+digc+WSiMhbkeG3iN79WY4Hwr9G05NpbhEVrsOYAg==
|
||||
devtools-protocol@0.0.869402:
|
||||
version "0.0.869402"
|
||||
resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.869402.tgz#03ade701761742e43ae4de5dc188bcd80f156d8d"
|
||||
integrity sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==
|
||||
|
||||
dicer@0.3.0:
|
||||
version "0.3.0"
|
||||
@@ -8912,7 +8912,7 @@ extract-files@9.0.0, extract-files@^9.0.0:
|
||||
resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-9.0.0.tgz#8a7744f2437f81f5ed3250ed9f1550de902fe54a"
|
||||
integrity sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==
|
||||
|
||||
extract-zip@2.0.1:
|
||||
extract-zip@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a"
|
||||
integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==
|
||||
@@ -10296,7 +10296,7 @@ https-browserify@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
|
||||
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
|
||||
|
||||
https-proxy-agent@5.0.0, https-proxy-agent@^5.0.0:
|
||||
https-proxy-agent@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2"
|
||||
integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==
|
||||
@@ -13950,13 +13950,6 @@ pirates@^4.0.0, pirates@^4.0.1:
|
||||
dependencies:
|
||||
node-modules-regexp "^1.0.0"
|
||||
|
||||
pkg-dir@4.2.0, pkg-dir@^4.1.0, pkg-dir@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
|
||||
integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
|
||||
dependencies:
|
||||
find-up "^4.0.0"
|
||||
|
||||
pkg-dir@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b"
|
||||
@@ -13971,6 +13964,13 @@ pkg-dir@^3.0.0:
|
||||
dependencies:
|
||||
find-up "^3.0.0"
|
||||
|
||||
pkg-dir@^4.1.0, pkg-dir@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
|
||||
integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
|
||||
dependencies:
|
||||
find-up "^4.0.0"
|
||||
|
||||
pkg-up@2.0.0, pkg-up@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f"
|
||||
@@ -14503,12 +14503,7 @@ process@^0.11.10:
|
||||
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
|
||||
integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
|
||||
|
||||
progress@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.1.tgz#c9242169342b1c29d275889c95734621b1952e31"
|
||||
integrity sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==
|
||||
|
||||
progress@^2.0.0, progress@^2.0.3:
|
||||
progress@^2.0.0, progress@^2.0.1, progress@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
||||
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
||||
@@ -14723,7 +14718,7 @@ proxy-addr@~2.0.5:
|
||||
forwarded "0.2.0"
|
||||
ipaddr.js "1.9.1"
|
||||
|
||||
proxy-from-env@1.1.0:
|
||||
proxy-from-env@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
||||
@@ -14804,41 +14799,41 @@ punycode@^2.1.0, punycode@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||
|
||||
puppeteer-core@^10.1.0:
|
||||
version "10.1.0"
|
||||
resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-10.1.0.tgz#ffd9fd780ad237b9ac23cc95cbb919be5e4523a5"
|
||||
integrity sha512-x2yDSJI/PRiWhDqAt1jd4rhTotxwjwKzHLIIqD2MlJ+TmzGJfBY9snAGIVXJwkWfKJg+Ef5xupdK0EbHDqBpFw==
|
||||
puppeteer-core@^9.1.1:
|
||||
version "9.1.1"
|
||||
resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-9.1.1.tgz#0c189c3275967d65c39270e6b146e559baca3d47"
|
||||
integrity sha512-zbedbitVIGhmgz0nt7eIdLsnaoVZSlNJfBivqm2w67T8LR2bU1dvnruDZ8nQO0zn++Iet7zHbAOdnuS5+H2E7A==
|
||||
dependencies:
|
||||
debug "4.3.1"
|
||||
devtools-protocol "0.0.883894"
|
||||
extract-zip "2.0.1"
|
||||
https-proxy-agent "5.0.0"
|
||||
node-fetch "2.6.1"
|
||||
pkg-dir "4.2.0"
|
||||
progress "2.0.1"
|
||||
proxy-from-env "1.1.0"
|
||||
rimraf "3.0.2"
|
||||
tar-fs "2.0.0"
|
||||
unbzip2-stream "1.3.3"
|
||||
ws "7.4.6"
|
||||
debug "^4.1.0"
|
||||
devtools-protocol "0.0.869402"
|
||||
extract-zip "^2.0.0"
|
||||
https-proxy-agent "^5.0.0"
|
||||
node-fetch "^2.6.1"
|
||||
pkg-dir "^4.2.0"
|
||||
progress "^2.0.1"
|
||||
proxy-from-env "^1.1.0"
|
||||
rimraf "^3.0.2"
|
||||
tar-fs "^2.0.0"
|
||||
unbzip2-stream "^1.3.3"
|
||||
ws "^7.2.3"
|
||||
|
||||
puppeteer@^10.1.0:
|
||||
version "10.1.0"
|
||||
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-10.1.0.tgz#6ee1d7e30401a967f4403bd42ace9e51e399504f"
|
||||
integrity sha512-bsyDHbFBvbofZ63xqF7hMhuKBX1h4WsqFIAoh1GuHr/Y9cewh+EFNAOdqWSkQRHLiBU/MY6M+8PUnXXjAPtuSg==
|
||||
puppeteer@^9.1.1:
|
||||
version "9.1.1"
|
||||
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-9.1.1.tgz#f74b7facf86887efd6c6b9fabb7baae6fdce012c"
|
||||
integrity sha512-W+nOulP2tYd/ZG99WuZC/I5ljjQQ7EUw/jQGcIb9eu8mDlZxNY2SgcJXTLG9h5gRvqA3uJOe4hZXYsd3EqioMw==
|
||||
dependencies:
|
||||
debug "4.3.1"
|
||||
devtools-protocol "0.0.883894"
|
||||
extract-zip "2.0.1"
|
||||
https-proxy-agent "5.0.0"
|
||||
node-fetch "2.6.1"
|
||||
pkg-dir "4.2.0"
|
||||
progress "2.0.1"
|
||||
proxy-from-env "1.1.0"
|
||||
rimraf "3.0.2"
|
||||
tar-fs "2.0.0"
|
||||
unbzip2-stream "1.3.3"
|
||||
ws "7.4.6"
|
||||
debug "^4.1.0"
|
||||
devtools-protocol "0.0.869402"
|
||||
extract-zip "^2.0.0"
|
||||
https-proxy-agent "^5.0.0"
|
||||
node-fetch "^2.6.1"
|
||||
pkg-dir "^4.2.0"
|
||||
progress "^2.0.1"
|
||||
proxy-from-env "^1.1.0"
|
||||
rimraf "^3.0.2"
|
||||
tar-fs "^2.0.0"
|
||||
unbzip2-stream "^1.3.3"
|
||||
ws "^7.2.3"
|
||||
|
||||
purgecss@^4.0.3:
|
||||
version "4.0.3"
|
||||
@@ -17053,17 +17048,7 @@ tapable@^2.0.0, tapable@^2.2.0:
|
||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b"
|
||||
integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==
|
||||
|
||||
tar-fs@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad"
|
||||
integrity sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==
|
||||
dependencies:
|
||||
chownr "^1.1.1"
|
||||
mkdirp "^0.5.1"
|
||||
pump "^3.0.0"
|
||||
tar-stream "^2.0.0"
|
||||
|
||||
tar-fs@^2.1.1:
|
||||
tar-fs@^2.0.0, tar-fs@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784"
|
||||
integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==
|
||||
@@ -17073,7 +17058,7 @@ tar-fs@^2.1.1:
|
||||
pump "^3.0.0"
|
||||
tar-stream "^2.1.4"
|
||||
|
||||
tar-stream@^2.0.0, tar-stream@^2.1.2, tar-stream@^2.1.4:
|
||||
tar-stream@^2.1.2, tar-stream@^2.1.4:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
|
||||
integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
|
||||
@@ -17616,10 +17601,10 @@ unbox-primitive@^1.0.1:
|
||||
has-symbols "^1.0.2"
|
||||
which-boxed-primitive "^1.0.2"
|
||||
|
||||
unbzip2-stream@1.3.3:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz#d156d205e670d8d8c393e1c02ebd506422873f6a"
|
||||
integrity sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==
|
||||
unbzip2-stream@^1.3.3:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7"
|
||||
integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==
|
||||
dependencies:
|
||||
buffer "^5.2.1"
|
||||
through "^2.3.8"
|
||||
@@ -18438,11 +18423,6 @@ ws@7.4.5:
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.5.tgz#a484dd851e9beb6fdb420027e3885e8ce48986c1"
|
||||
integrity sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==
|
||||
|
||||
ws@7.4.6:
|
||||
version "7.4.6"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
|
||||
integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
|
||||
|
||||
"ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^7.4.5:
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.0.tgz#0033bafea031fb9df041b2026fc72a571ca44691"
|
||||
@@ -18455,6 +18435,11 @@ ws@^6.0.0, ws@^6.2.1:
|
||||
dependencies:
|
||||
async-limiter "~1.0.0"
|
||||
|
||||
ws@^7.2.3:
|
||||
version "7.5.3"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74"
|
||||
integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==
|
||||
|
||||
"x----x----x@^0 RedwoodJS", "x----x----x@^0 Types":
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/x----x----x/-/x----x----x-0.0.2.tgz#44e014e79f53205066ecf7d170dd1f30ef677856"
|
||||
|
||||
Reference in New Issue
Block a user