├── .codesandbox └── tasks.json ├── .firebaserc ├── .gitignore ├── .pnp.cjs ├── .pnp.loader.mjs ├── .yarn ├── install-state.gz └── releases │ └── yarn-4.0.1.cjs ├── .yarnrc.yml ├── LICENSE ├── README.md ├── __mocks__ ├── @excalidraw │ └── excalidraw.js ├── points-on-curve.js └── roughjs │ └── bin │ ├── math.js │ └── rough.js ├── firebase.json ├── firestore.indexes.json ├── firestore.rules ├── index.html ├── package.json ├── postcss.config.js ├── public └── index.html ├── screenshot.png ├── src ├── environment-interface │ ├── authentication.ts │ ├── copyImageToClipboard.ts │ ├── index.tsx │ ├── loom.ts │ └── storage.ts ├── environments │ ├── authentication │ │ ├── browser.ts │ │ └── test.ts │ ├── browser.ts │ ├── copyImageToClipboard │ │ ├── browser.ts │ │ └── test.ts │ ├── loom │ │ ├── browser.ts │ │ └── test.ts │ ├── storage │ │ ├── browser.ts │ │ └── test.ts │ └── test.ts ├── favicon.svg ├── firebase.config.json ├── index.css ├── logo.svg ├── main.tsx ├── pages │ ├── dashboard │ │ ├── Dashboard.tsx │ │ ├── ExcalidrawPreview.tsx │ │ ├── Navigation.tsx │ │ ├── index.tsx │ │ ├── useDashboard.test.tsx │ │ ├── useDashboard.tsx │ │ ├── useNavigation.test.tsx │ │ ├── useNavigation.tsx │ │ └── useUserDashboard.tsx │ ├── excalidraw │ │ ├── Excalidraw.tsx │ │ ├── ExcalidrawCanvas.tsx │ │ ├── index.tsx │ │ ├── useExcalidraw │ │ │ ├── index.test.tsx │ │ │ ├── index.tsx │ │ │ ├── reducer.ts │ │ │ └── types.ts │ │ └── useRecording.tsx │ ├── index.tsx │ ├── useAuth.test.tsx │ └── useAuth.tsx └── utils.ts ├── tailwind.config.js ├── tsconfig.json ├── vite.config.ts └── yarn.lock /.codesandbox/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // These tasks will run in order when initializing your CodeSandbox project. 3 | "setupTasks": [ 4 | { 5 | "name": "Install Dependencies", 6 | "command": "yarn install" 7 | } 8 | ], 9 | 10 | // These tasks can be run from CodeSandbox. Running one will open a log in the app. 11 | "tasks": { 12 | "auth": { 13 | "name": "auth", 14 | "command": "npx firebase login --no-localhost", 15 | "preview": { 16 | "port": 9005 17 | } 18 | }, 19 | "dev": { 20 | "name": "dev", 21 | "command": "yarn dev", 22 | "runAtStart": true, 23 | "preview": { 24 | "port": 3000 25 | } 26 | }, 27 | "build": { 28 | "name": "build", 29 | "command": "yarn build", 30 | "runAtStart": false 31 | }, 32 | "deploy": { 33 | "name": "deploy", 34 | "command": "yarn deploy", 35 | "runAtStart": false 36 | }, 37 | "serve": { 38 | "name": "serve", 39 | "command": "yarn serve", 40 | "runAtStart": false 41 | }, 42 | "test": { 43 | "name": "test", 44 | "command": "yarn test", 45 | "runAtStart": false 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "excalidraw-8b385" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .DS_Store 9 | .firebase/** 10 | ssl.crt 11 | ssl.key 12 | 13 | # Diagnostic reports (https://nodejs.org/api/report.html) 14 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | lib-cov 24 | 25 | # Coverage directory used by tools like istanbul 26 | coverage 27 | *.lcov 28 | 29 | # nyc test coverage 30 | .nyc_output 31 | 32 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 33 | .grunt 34 | 35 | # Bower dependency directory (https://bower.io/) 36 | bower_components 37 | 38 | # node-waf configuration 39 | .lock-wscript 40 | 41 | # Compiled binary addons (https://nodejs.org/api/addons.html) 42 | build/Release 43 | 44 | # Dependency directories 45 | node_modules/ 46 | jspm_packages/ 47 | 48 | # TypeScript v1 declaration files 49 | typings/ 50 | 51 | # TypeScript cache 52 | *.tsbuildinfo 53 | 54 | # Optional npm cache directory 55 | .npm 56 | 57 | # Optional eslint cache 58 | .eslintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variables file 76 | .env 77 | .env.test 78 | 79 | # parcel-bundler cache (https://parceljs.org/) 80 | .cache 81 | 82 | # Next.js build output 83 | .next 84 | 85 | # Nuxt.js build / generate output 86 | .nuxt 87 | dist 88 | 89 | # Gatsby files 90 | .cache/ 91 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 92 | # https://nextjs.org/blog/next-9-1#public-directory-support 93 | # public 94 | 95 | # vuepress build output 96 | .vuepress/dist 97 | 98 | # Serverless directories 99 | .serverless/ 100 | 101 | # FuseBox cache 102 | .fusebox/ 103 | 104 | # DynamoDB Local files 105 | .dynamodb/ 106 | 107 | # TernJS port file 108 | .tern-port 109 | -------------------------------------------------------------------------------- /.pnp.loader.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import { URL as URL$1, fileURLToPath, pathToFileURL } from 'url'; 3 | import path from 'path'; 4 | import { createHash } from 'crypto'; 5 | import { EOL } from 'os'; 6 | import moduleExports, { isBuiltin } from 'module'; 7 | import assert from 'assert'; 8 | 9 | const SAFE_TIME = 456789e3; 10 | 11 | const PortablePath = { 12 | root: `/`, 13 | dot: `.`, 14 | parent: `..` 15 | }; 16 | const npath = Object.create(path); 17 | const ppath = Object.create(path.posix); 18 | npath.cwd = () => process.cwd(); 19 | ppath.cwd = process.platform === `win32` ? () => toPortablePath(process.cwd()) : process.cwd; 20 | if (process.platform === `win32`) { 21 | ppath.resolve = (...segments) => { 22 | if (segments.length > 0 && ppath.isAbsolute(segments[0])) { 23 | return path.posix.resolve(...segments); 24 | } else { 25 | return path.posix.resolve(ppath.cwd(), ...segments); 26 | } 27 | }; 28 | } 29 | const contains = function(pathUtils, from, to) { 30 | from = pathUtils.normalize(from); 31 | to = pathUtils.normalize(to); 32 | if (from === to) 33 | return `.`; 34 | if (!from.endsWith(pathUtils.sep)) 35 | from = from + pathUtils.sep; 36 | if (to.startsWith(from)) { 37 | return to.slice(from.length); 38 | } else { 39 | return null; 40 | } 41 | }; 42 | npath.contains = (from, to) => contains(npath, from, to); 43 | ppath.contains = (from, to) => contains(ppath, from, to); 44 | const WINDOWS_PATH_REGEXP = /^([a-zA-Z]:.*)$/; 45 | const UNC_WINDOWS_PATH_REGEXP = /^\/\/(\.\/)?(.*)$/; 46 | const PORTABLE_PATH_REGEXP = /^\/([a-zA-Z]:.*)$/; 47 | const UNC_PORTABLE_PATH_REGEXP = /^\/unc\/(\.dot\/)?(.*)$/; 48 | function fromPortablePathWin32(p) { 49 | let portablePathMatch, uncPortablePathMatch; 50 | if (portablePathMatch = p.match(PORTABLE_PATH_REGEXP)) 51 | p = portablePathMatch[1]; 52 | else if (uncPortablePathMatch = p.match(UNC_PORTABLE_PATH_REGEXP)) 53 | p = `\\\\${uncPortablePathMatch[1] ? `.\\` : ``}${uncPortablePathMatch[2]}`; 54 | else 55 | return p; 56 | return p.replace(/\//g, `\\`); 57 | } 58 | function toPortablePathWin32(p) { 59 | p = p.replace(/\\/g, `/`); 60 | let windowsPathMatch, uncWindowsPathMatch; 61 | if (windowsPathMatch = p.match(WINDOWS_PATH_REGEXP)) 62 | p = `/${windowsPathMatch[1]}`; 63 | else if (uncWindowsPathMatch = p.match(UNC_WINDOWS_PATH_REGEXP)) 64 | p = `/unc/${uncWindowsPathMatch[1] ? `.dot/` : ``}${uncWindowsPathMatch[2]}`; 65 | return p; 66 | } 67 | const toPortablePath = process.platform === `win32` ? toPortablePathWin32 : (p) => p; 68 | const fromPortablePath = process.platform === `win32` ? fromPortablePathWin32 : (p) => p; 69 | npath.fromPortablePath = fromPortablePath; 70 | npath.toPortablePath = toPortablePath; 71 | function convertPath(targetPathUtils, sourcePath) { 72 | return targetPathUtils === npath ? fromPortablePath(sourcePath) : toPortablePath(sourcePath); 73 | } 74 | 75 | const defaultTime = new Date(SAFE_TIME * 1e3); 76 | const defaultTimeMs = defaultTime.getTime(); 77 | async function copyPromise(destinationFs, destination, sourceFs, source, opts) { 78 | const normalizedDestination = destinationFs.pathUtils.normalize(destination); 79 | const normalizedSource = sourceFs.pathUtils.normalize(source); 80 | const prelayout = []; 81 | const postlayout = []; 82 | const { atime, mtime } = opts.stableTime ? { atime: defaultTime, mtime: defaultTime } : await sourceFs.lstatPromise(normalizedSource); 83 | await destinationFs.mkdirpPromise(destinationFs.pathUtils.dirname(destination), { utimes: [atime, mtime] }); 84 | await copyImpl(prelayout, postlayout, destinationFs, normalizedDestination, sourceFs, normalizedSource, { ...opts, didParentExist: true }); 85 | for (const operation of prelayout) 86 | await operation(); 87 | await Promise.all(postlayout.map((operation) => { 88 | return operation(); 89 | })); 90 | } 91 | async function copyImpl(prelayout, postlayout, destinationFs, destination, sourceFs, source, opts) { 92 | const destinationStat = opts.didParentExist ? await maybeLStat(destinationFs, destination) : null; 93 | const sourceStat = await sourceFs.lstatPromise(source); 94 | const { atime, mtime } = opts.stableTime ? { atime: defaultTime, mtime: defaultTime } : sourceStat; 95 | let updated; 96 | switch (true) { 97 | case sourceStat.isDirectory(): 98 | { 99 | updated = await copyFolder(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts); 100 | } 101 | break; 102 | case sourceStat.isFile(): 103 | { 104 | updated = await copyFile(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts); 105 | } 106 | break; 107 | case sourceStat.isSymbolicLink(): 108 | { 109 | updated = await copySymlink(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts); 110 | } 111 | break; 112 | default: 113 | { 114 | throw new Error(`Unsupported file type (${sourceStat.mode})`); 115 | } 116 | } 117 | if (opts.linkStrategy?.type !== `HardlinkFromIndex` || !sourceStat.isFile()) { 118 | if (updated || destinationStat?.mtime?.getTime() !== mtime.getTime() || destinationStat?.atime?.getTime() !== atime.getTime()) { 119 | postlayout.push(() => destinationFs.lutimesPromise(destination, atime, mtime)); 120 | updated = true; 121 | } 122 | if (destinationStat === null || (destinationStat.mode & 511) !== (sourceStat.mode & 511)) { 123 | postlayout.push(() => destinationFs.chmodPromise(destination, sourceStat.mode & 511)); 124 | updated = true; 125 | } 126 | } 127 | return updated; 128 | } 129 | async function maybeLStat(baseFs, p) { 130 | try { 131 | return await baseFs.lstatPromise(p); 132 | } catch (e) { 133 | return null; 134 | } 135 | } 136 | async function copyFolder(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) { 137 | if (destinationStat !== null && !destinationStat.isDirectory()) { 138 | if (opts.overwrite) { 139 | prelayout.push(async () => destinationFs.removePromise(destination)); 140 | destinationStat = null; 141 | } else { 142 | return false; 143 | } 144 | } 145 | let updated = false; 146 | if (destinationStat === null) { 147 | prelayout.push(async () => { 148 | try { 149 | await destinationFs.mkdirPromise(destination, { mode: sourceStat.mode }); 150 | } catch (err) { 151 | if (err.code !== `EEXIST`) { 152 | throw err; 153 | } 154 | } 155 | }); 156 | updated = true; 157 | } 158 | const entries = await sourceFs.readdirPromise(source); 159 | const nextOpts = opts.didParentExist && !destinationStat ? { ...opts, didParentExist: false } : opts; 160 | if (opts.stableSort) { 161 | for (const entry of entries.sort()) { 162 | if (await copyImpl(prelayout, postlayout, destinationFs, destinationFs.pathUtils.join(destination, entry), sourceFs, sourceFs.pathUtils.join(source, entry), nextOpts)) { 163 | updated = true; 164 | } 165 | } 166 | } else { 167 | const entriesUpdateStatus = await Promise.all(entries.map(async (entry) => { 168 | await copyImpl(prelayout, postlayout, destinationFs, destinationFs.pathUtils.join(destination, entry), sourceFs, sourceFs.pathUtils.join(source, entry), nextOpts); 169 | })); 170 | if (entriesUpdateStatus.some((status) => status)) { 171 | updated = true; 172 | } 173 | } 174 | return updated; 175 | } 176 | async function copyFileViaIndex(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts, linkStrategy) { 177 | const sourceHash = await sourceFs.checksumFilePromise(source, { algorithm: `sha1` }); 178 | const indexPath = destinationFs.pathUtils.join(linkStrategy.indexPath, sourceHash.slice(0, 2), `${sourceHash}.dat`); 179 | let AtomicBehavior; 180 | ((AtomicBehavior2) => { 181 | AtomicBehavior2[AtomicBehavior2["Lock"] = 0] = "Lock"; 182 | AtomicBehavior2[AtomicBehavior2["Rename"] = 1] = "Rename"; 183 | })(AtomicBehavior || (AtomicBehavior = {})); 184 | let atomicBehavior = 1 /* Rename */; 185 | let indexStat = await maybeLStat(destinationFs, indexPath); 186 | if (destinationStat) { 187 | const isDestinationHardlinkedFromIndex = indexStat && destinationStat.dev === indexStat.dev && destinationStat.ino === indexStat.ino; 188 | const isIndexModified = indexStat?.mtimeMs !== defaultTimeMs; 189 | if (isDestinationHardlinkedFromIndex) { 190 | if (isIndexModified && linkStrategy.autoRepair) { 191 | atomicBehavior = 0 /* Lock */; 192 | indexStat = null; 193 | } 194 | } 195 | if (!isDestinationHardlinkedFromIndex) { 196 | if (opts.overwrite) { 197 | prelayout.push(async () => destinationFs.removePromise(destination)); 198 | destinationStat = null; 199 | } else { 200 | return false; 201 | } 202 | } 203 | } 204 | const tempPath = !indexStat && atomicBehavior === 1 /* Rename */ ? `${indexPath}.${Math.floor(Math.random() * 4294967296).toString(16).padStart(8, `0`)}` : null; 205 | let tempPathCleaned = false; 206 | prelayout.push(async () => { 207 | if (!indexStat) { 208 | if (atomicBehavior === 0 /* Lock */) { 209 | await destinationFs.lockPromise(indexPath, async () => { 210 | const content = await sourceFs.readFilePromise(source); 211 | await destinationFs.writeFilePromise(indexPath, content); 212 | }); 213 | } 214 | if (atomicBehavior === 1 /* Rename */ && tempPath) { 215 | const content = await sourceFs.readFilePromise(source); 216 | await destinationFs.writeFilePromise(tempPath, content); 217 | try { 218 | await destinationFs.linkPromise(tempPath, indexPath); 219 | } catch (err) { 220 | if (err.code === `EEXIST`) { 221 | tempPathCleaned = true; 222 | await destinationFs.unlinkPromise(tempPath); 223 | } else { 224 | throw err; 225 | } 226 | } 227 | } 228 | } 229 | if (!destinationStat) { 230 | await destinationFs.linkPromise(indexPath, destination); 231 | } 232 | }); 233 | postlayout.push(async () => { 234 | if (!indexStat) 235 | await destinationFs.lutimesPromise(indexPath, defaultTime, defaultTime); 236 | if (tempPath && !tempPathCleaned) { 237 | await destinationFs.unlinkPromise(tempPath); 238 | } 239 | }); 240 | return false; 241 | } 242 | async function copyFileDirect(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) { 243 | if (destinationStat !== null) { 244 | if (opts.overwrite) { 245 | prelayout.push(async () => destinationFs.removePromise(destination)); 246 | destinationStat = null; 247 | } else { 248 | return false; 249 | } 250 | } 251 | prelayout.push(async () => { 252 | const content = await sourceFs.readFilePromise(source); 253 | await destinationFs.writeFilePromise(destination, content); 254 | }); 255 | return true; 256 | } 257 | async function copyFile(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) { 258 | if (opts.linkStrategy?.type === `HardlinkFromIndex`) { 259 | return copyFileViaIndex(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts, opts.linkStrategy); 260 | } else { 261 | return copyFileDirect(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts); 262 | } 263 | } 264 | async function copySymlink(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) { 265 | if (destinationStat !== null) { 266 | if (opts.overwrite) { 267 | prelayout.push(async () => destinationFs.removePromise(destination)); 268 | destinationStat = null; 269 | } else { 270 | return false; 271 | } 272 | } 273 | prelayout.push(async () => { 274 | await destinationFs.symlinkPromise(convertPath(destinationFs.pathUtils, await sourceFs.readlinkPromise(source)), destination); 275 | }); 276 | return true; 277 | } 278 | 279 | class FakeFS { 280 | constructor(pathUtils) { 281 | this.pathUtils = pathUtils; 282 | } 283 | async *genTraversePromise(init, { stableSort = false } = {}) { 284 | const stack = [init]; 285 | while (stack.length > 0) { 286 | const p = stack.shift(); 287 | const entry = await this.lstatPromise(p); 288 | if (entry.isDirectory()) { 289 | const entries = await this.readdirPromise(p); 290 | if (stableSort) { 291 | for (const entry2 of entries.sort()) { 292 | stack.push(this.pathUtils.join(p, entry2)); 293 | } 294 | } else { 295 | throw new Error(`Not supported`); 296 | } 297 | } else { 298 | yield p; 299 | } 300 | } 301 | } 302 | async checksumFilePromise(path, { algorithm = `sha512` } = {}) { 303 | const fd = await this.openPromise(path, `r`); 304 | try { 305 | const CHUNK_SIZE = 65536; 306 | const chunk = Buffer.allocUnsafeSlow(CHUNK_SIZE); 307 | const hash = createHash(algorithm); 308 | let bytesRead = 0; 309 | while ((bytesRead = await this.readPromise(fd, chunk, 0, CHUNK_SIZE)) !== 0) 310 | hash.update(bytesRead === CHUNK_SIZE ? chunk : chunk.slice(0, bytesRead)); 311 | return hash.digest(`hex`); 312 | } finally { 313 | await this.closePromise(fd); 314 | } 315 | } 316 | async removePromise(p, { recursive = true, maxRetries = 5 } = {}) { 317 | let stat; 318 | try { 319 | stat = await this.lstatPromise(p); 320 | } catch (error) { 321 | if (error.code === `ENOENT`) { 322 | return; 323 | } else { 324 | throw error; 325 | } 326 | } 327 | if (stat.isDirectory()) { 328 | if (recursive) { 329 | const entries = await this.readdirPromise(p); 330 | await Promise.all(entries.map((entry) => { 331 | return this.removePromise(this.pathUtils.resolve(p, entry)); 332 | })); 333 | } 334 | for (let t = 0; t <= maxRetries; t++) { 335 | try { 336 | await this.rmdirPromise(p); 337 | break; 338 | } catch (error) { 339 | if (error.code !== `EBUSY` && error.code !== `ENOTEMPTY`) { 340 | throw error; 341 | } else if (t < maxRetries) { 342 | await new Promise((resolve) => setTimeout(resolve, t * 100)); 343 | } 344 | } 345 | } 346 | } else { 347 | await this.unlinkPromise(p); 348 | } 349 | } 350 | removeSync(p, { recursive = true } = {}) { 351 | let stat; 352 | try { 353 | stat = this.lstatSync(p); 354 | } catch (error) { 355 | if (error.code === `ENOENT`) { 356 | return; 357 | } else { 358 | throw error; 359 | } 360 | } 361 | if (stat.isDirectory()) { 362 | if (recursive) 363 | for (const entry of this.readdirSync(p)) 364 | this.removeSync(this.pathUtils.resolve(p, entry)); 365 | this.rmdirSync(p); 366 | } else { 367 | this.unlinkSync(p); 368 | } 369 | } 370 | async mkdirpPromise(p, { chmod, utimes } = {}) { 371 | p = this.resolve(p); 372 | if (p === this.pathUtils.dirname(p)) 373 | return void 0; 374 | const parts = p.split(this.pathUtils.sep); 375 | let createdDirectory; 376 | for (let u = 2; u <= parts.length; ++u) { 377 | const subPath = parts.slice(0, u).join(this.pathUtils.sep); 378 | if (!this.existsSync(subPath)) { 379 | try { 380 | await this.mkdirPromise(subPath); 381 | } catch (error) { 382 | if (error.code === `EEXIST`) { 383 | continue; 384 | } else { 385 | throw error; 386 | } 387 | } 388 | createdDirectory ??= subPath; 389 | if (chmod != null) 390 | await this.chmodPromise(subPath, chmod); 391 | if (utimes != null) { 392 | await this.utimesPromise(subPath, utimes[0], utimes[1]); 393 | } else { 394 | const parentStat = await this.statPromise(this.pathUtils.dirname(subPath)); 395 | await this.utimesPromise(subPath, parentStat.atime, parentStat.mtime); 396 | } 397 | } 398 | } 399 | return createdDirectory; 400 | } 401 | mkdirpSync(p, { chmod, utimes } = {}) { 402 | p = this.resolve(p); 403 | if (p === this.pathUtils.dirname(p)) 404 | return void 0; 405 | const parts = p.split(this.pathUtils.sep); 406 | let createdDirectory; 407 | for (let u = 2; u <= parts.length; ++u) { 408 | const subPath = parts.slice(0, u).join(this.pathUtils.sep); 409 | if (!this.existsSync(subPath)) { 410 | try { 411 | this.mkdirSync(subPath); 412 | } catch (error) { 413 | if (error.code === `EEXIST`) { 414 | continue; 415 | } else { 416 | throw error; 417 | } 418 | } 419 | createdDirectory ??= subPath; 420 | if (chmod != null) 421 | this.chmodSync(subPath, chmod); 422 | if (utimes != null) { 423 | this.utimesSync(subPath, utimes[0], utimes[1]); 424 | } else { 425 | const parentStat = this.statSync(this.pathUtils.dirname(subPath)); 426 | this.utimesSync(subPath, parentStat.atime, parentStat.mtime); 427 | } 428 | } 429 | } 430 | return createdDirectory; 431 | } 432 | async copyPromise(destination, source, { baseFs = this, overwrite = true, stableSort = false, stableTime = false, linkStrategy = null } = {}) { 433 | return await copyPromise(this, destination, baseFs, source, { overwrite, stableSort, stableTime, linkStrategy }); 434 | } 435 | copySync(destination, source, { baseFs = this, overwrite = true } = {}) { 436 | const stat = baseFs.lstatSync(source); 437 | const exists = this.existsSync(destination); 438 | if (stat.isDirectory()) { 439 | this.mkdirpSync(destination); 440 | const directoryListing = baseFs.readdirSync(source); 441 | for (const entry of directoryListing) { 442 | this.copySync(this.pathUtils.join(destination, entry), baseFs.pathUtils.join(source, entry), { baseFs, overwrite }); 443 | } 444 | } else if (stat.isFile()) { 445 | if (!exists || overwrite) { 446 | if (exists) 447 | this.removeSync(destination); 448 | const content = baseFs.readFileSync(source); 449 | this.writeFileSync(destination, content); 450 | } 451 | } else if (stat.isSymbolicLink()) { 452 | if (!exists || overwrite) { 453 | if (exists) 454 | this.removeSync(destination); 455 | const target = baseFs.readlinkSync(source); 456 | this.symlinkSync(convertPath(this.pathUtils, target), destination); 457 | } 458 | } else { 459 | throw new Error(`Unsupported file type (file: ${source}, mode: 0o${stat.mode.toString(8).padStart(6, `0`)})`); 460 | } 461 | const mode = stat.mode & 511; 462 | this.chmodSync(destination, mode); 463 | } 464 | async changeFilePromise(p, content, opts = {}) { 465 | if (Buffer.isBuffer(content)) { 466 | return this.changeFileBufferPromise(p, content, opts); 467 | } else { 468 | return this.changeFileTextPromise(p, content, opts); 469 | } 470 | } 471 | async changeFileBufferPromise(p, content, { mode } = {}) { 472 | let current = Buffer.alloc(0); 473 | try { 474 | current = await this.readFilePromise(p); 475 | } catch (error) { 476 | } 477 | if (Buffer.compare(current, content) === 0) 478 | return; 479 | await this.writeFilePromise(p, content, { mode }); 480 | } 481 | async changeFileTextPromise(p, content, { automaticNewlines, mode } = {}) { 482 | let current = ``; 483 | try { 484 | current = await this.readFilePromise(p, `utf8`); 485 | } catch (error) { 486 | } 487 | const normalizedContent = automaticNewlines ? normalizeLineEndings(current, content) : content; 488 | if (current === normalizedContent) 489 | return; 490 | await this.writeFilePromise(p, normalizedContent, { mode }); 491 | } 492 | changeFileSync(p, content, opts = {}) { 493 | if (Buffer.isBuffer(content)) { 494 | return this.changeFileBufferSync(p, content, opts); 495 | } else { 496 | return this.changeFileTextSync(p, content, opts); 497 | } 498 | } 499 | changeFileBufferSync(p, content, { mode } = {}) { 500 | let current = Buffer.alloc(0); 501 | try { 502 | current = this.readFileSync(p); 503 | } catch (error) { 504 | } 505 | if (Buffer.compare(current, content) === 0) 506 | return; 507 | this.writeFileSync(p, content, { mode }); 508 | } 509 | changeFileTextSync(p, content, { automaticNewlines = false, mode } = {}) { 510 | let current = ``; 511 | try { 512 | current = this.readFileSync(p, `utf8`); 513 | } catch (error) { 514 | } 515 | const normalizedContent = automaticNewlines ? normalizeLineEndings(current, content) : content; 516 | if (current === normalizedContent) 517 | return; 518 | this.writeFileSync(p, normalizedContent, { mode }); 519 | } 520 | async movePromise(fromP, toP) { 521 | try { 522 | await this.renamePromise(fromP, toP); 523 | } catch (error) { 524 | if (error.code === `EXDEV`) { 525 | await this.copyPromise(toP, fromP); 526 | await this.removePromise(fromP); 527 | } else { 528 | throw error; 529 | } 530 | } 531 | } 532 | moveSync(fromP, toP) { 533 | try { 534 | this.renameSync(fromP, toP); 535 | } catch (error) { 536 | if (error.code === `EXDEV`) { 537 | this.copySync(toP, fromP); 538 | this.removeSync(fromP); 539 | } else { 540 | throw error; 541 | } 542 | } 543 | } 544 | async lockPromise(affectedPath, callback) { 545 | const lockPath = `${affectedPath}.flock`; 546 | const interval = 1e3 / 60; 547 | const startTime = Date.now(); 548 | let fd = null; 549 | const isAlive = async () => { 550 | let pid; 551 | try { 552 | [pid] = await this.readJsonPromise(lockPath); 553 | } catch (error) { 554 | return Date.now() - startTime < 500; 555 | } 556 | try { 557 | process.kill(pid, 0); 558 | return true; 559 | } catch (error) { 560 | return false; 561 | } 562 | }; 563 | while (fd === null) { 564 | try { 565 | fd = await this.openPromise(lockPath, `wx`); 566 | } catch (error) { 567 | if (error.code === `EEXIST`) { 568 | if (!await isAlive()) { 569 | try { 570 | await this.unlinkPromise(lockPath); 571 | continue; 572 | } catch (error2) { 573 | } 574 | } 575 | if (Date.now() - startTime < 60 * 1e3) { 576 | await new Promise((resolve) => setTimeout(resolve, interval)); 577 | } else { 578 | throw new Error(`Couldn't acquire a lock in a reasonable time (via ${lockPath})`); 579 | } 580 | } else { 581 | throw error; 582 | } 583 | } 584 | } 585 | await this.writePromise(fd, JSON.stringify([process.pid])); 586 | try { 587 | return await callback(); 588 | } finally { 589 | try { 590 | await this.closePromise(fd); 591 | await this.unlinkPromise(lockPath); 592 | } catch (error) { 593 | } 594 | } 595 | } 596 | async readJsonPromise(p) { 597 | const content = await this.readFilePromise(p, `utf8`); 598 | try { 599 | return JSON.parse(content); 600 | } catch (error) { 601 | error.message += ` (in ${p})`; 602 | throw error; 603 | } 604 | } 605 | readJsonSync(p) { 606 | const content = this.readFileSync(p, `utf8`); 607 | try { 608 | return JSON.parse(content); 609 | } catch (error) { 610 | error.message += ` (in ${p})`; 611 | throw error; 612 | } 613 | } 614 | async writeJsonPromise(p, data, { compact = false } = {}) { 615 | const space = compact ? 0 : 2; 616 | return await this.writeFilePromise(p, `${JSON.stringify(data, null, space)} 617 | `); 618 | } 619 | writeJsonSync(p, data, { compact = false } = {}) { 620 | const space = compact ? 0 : 2; 621 | return this.writeFileSync(p, `${JSON.stringify(data, null, space)} 622 | `); 623 | } 624 | async preserveTimePromise(p, cb) { 625 | const stat = await this.lstatPromise(p); 626 | const result = await cb(); 627 | if (typeof result !== `undefined`) 628 | p = result; 629 | await this.lutimesPromise(p, stat.atime, stat.mtime); 630 | } 631 | async preserveTimeSync(p, cb) { 632 | const stat = this.lstatSync(p); 633 | const result = cb(); 634 | if (typeof result !== `undefined`) 635 | p = result; 636 | this.lutimesSync(p, stat.atime, stat.mtime); 637 | } 638 | } 639 | class BasePortableFakeFS extends FakeFS { 640 | constructor() { 641 | super(ppath); 642 | } 643 | } 644 | function getEndOfLine(content) { 645 | const matches = content.match(/\r?\n/g); 646 | if (matches === null) 647 | return EOL; 648 | const crlf = matches.filter((nl) => nl === `\r 649 | `).length; 650 | const lf = matches.length - crlf; 651 | return crlf > lf ? `\r 652 | ` : ` 653 | `; 654 | } 655 | function normalizeLineEndings(originalContent, newContent) { 656 | return newContent.replace(/\r?\n/g, getEndOfLine(originalContent)); 657 | } 658 | 659 | class ProxiedFS extends FakeFS { 660 | getExtractHint(hints) { 661 | return this.baseFs.getExtractHint(hints); 662 | } 663 | resolve(path) { 664 | return this.mapFromBase(this.baseFs.resolve(this.mapToBase(path))); 665 | } 666 | getRealPath() { 667 | return this.mapFromBase(this.baseFs.getRealPath()); 668 | } 669 | async openPromise(p, flags, mode) { 670 | return this.baseFs.openPromise(this.mapToBase(p), flags, mode); 671 | } 672 | openSync(p, flags, mode) { 673 | return this.baseFs.openSync(this.mapToBase(p), flags, mode); 674 | } 675 | async opendirPromise(p, opts) { 676 | return Object.assign(await this.baseFs.opendirPromise(this.mapToBase(p), opts), { path: p }); 677 | } 678 | opendirSync(p, opts) { 679 | return Object.assign(this.baseFs.opendirSync(this.mapToBase(p), opts), { path: p }); 680 | } 681 | async readPromise(fd, buffer, offset, length, position) { 682 | return await this.baseFs.readPromise(fd, buffer, offset, length, position); 683 | } 684 | readSync(fd, buffer, offset, length, position) { 685 | return this.baseFs.readSync(fd, buffer, offset, length, position); 686 | } 687 | async writePromise(fd, buffer, offset, length, position) { 688 | if (typeof buffer === `string`) { 689 | return await this.baseFs.writePromise(fd, buffer, offset); 690 | } else { 691 | return await this.baseFs.writePromise(fd, buffer, offset, length, position); 692 | } 693 | } 694 | writeSync(fd, buffer, offset, length, position) { 695 | if (typeof buffer === `string`) { 696 | return this.baseFs.writeSync(fd, buffer, offset); 697 | } else { 698 | return this.baseFs.writeSync(fd, buffer, offset, length, position); 699 | } 700 | } 701 | async closePromise(fd) { 702 | return this.baseFs.closePromise(fd); 703 | } 704 | closeSync(fd) { 705 | this.baseFs.closeSync(fd); 706 | } 707 | createReadStream(p, opts) { 708 | return this.baseFs.createReadStream(p !== null ? this.mapToBase(p) : p, opts); 709 | } 710 | createWriteStream(p, opts) { 711 | return this.baseFs.createWriteStream(p !== null ? this.mapToBase(p) : p, opts); 712 | } 713 | async realpathPromise(p) { 714 | return this.mapFromBase(await this.baseFs.realpathPromise(this.mapToBase(p))); 715 | } 716 | realpathSync(p) { 717 | return this.mapFromBase(this.baseFs.realpathSync(this.mapToBase(p))); 718 | } 719 | async existsPromise(p) { 720 | return this.baseFs.existsPromise(this.mapToBase(p)); 721 | } 722 | existsSync(p) { 723 | return this.baseFs.existsSync(this.mapToBase(p)); 724 | } 725 | accessSync(p, mode) { 726 | return this.baseFs.accessSync(this.mapToBase(p), mode); 727 | } 728 | async accessPromise(p, mode) { 729 | return this.baseFs.accessPromise(this.mapToBase(p), mode); 730 | } 731 | async statPromise(p, opts) { 732 | return this.baseFs.statPromise(this.mapToBase(p), opts); 733 | } 734 | statSync(p, opts) { 735 | return this.baseFs.statSync(this.mapToBase(p), opts); 736 | } 737 | async fstatPromise(fd, opts) { 738 | return this.baseFs.fstatPromise(fd, opts); 739 | } 740 | fstatSync(fd, opts) { 741 | return this.baseFs.fstatSync(fd, opts); 742 | } 743 | lstatPromise(p, opts) { 744 | return this.baseFs.lstatPromise(this.mapToBase(p), opts); 745 | } 746 | lstatSync(p, opts) { 747 | return this.baseFs.lstatSync(this.mapToBase(p), opts); 748 | } 749 | async fchmodPromise(fd, mask) { 750 | return this.baseFs.fchmodPromise(fd, mask); 751 | } 752 | fchmodSync(fd, mask) { 753 | return this.baseFs.fchmodSync(fd, mask); 754 | } 755 | async chmodPromise(p, mask) { 756 | return this.baseFs.chmodPromise(this.mapToBase(p), mask); 757 | } 758 | chmodSync(p, mask) { 759 | return this.baseFs.chmodSync(this.mapToBase(p), mask); 760 | } 761 | async fchownPromise(fd, uid, gid) { 762 | return this.baseFs.fchownPromise(fd, uid, gid); 763 | } 764 | fchownSync(fd, uid, gid) { 765 | return this.baseFs.fchownSync(fd, uid, gid); 766 | } 767 | async chownPromise(p, uid, gid) { 768 | return this.baseFs.chownPromise(this.mapToBase(p), uid, gid); 769 | } 770 | chownSync(p, uid, gid) { 771 | return this.baseFs.chownSync(this.mapToBase(p), uid, gid); 772 | } 773 | async renamePromise(oldP, newP) { 774 | return this.baseFs.renamePromise(this.mapToBase(oldP), this.mapToBase(newP)); 775 | } 776 | renameSync(oldP, newP) { 777 | return this.baseFs.renameSync(this.mapToBase(oldP), this.mapToBase(newP)); 778 | } 779 | async copyFilePromise(sourceP, destP, flags = 0) { 780 | return this.baseFs.copyFilePromise(this.mapToBase(sourceP), this.mapToBase(destP), flags); 781 | } 782 | copyFileSync(sourceP, destP, flags = 0) { 783 | return this.baseFs.copyFileSync(this.mapToBase(sourceP), this.mapToBase(destP), flags); 784 | } 785 | async appendFilePromise(p, content, opts) { 786 | return this.baseFs.appendFilePromise(this.fsMapToBase(p), content, opts); 787 | } 788 | appendFileSync(p, content, opts) { 789 | return this.baseFs.appendFileSync(this.fsMapToBase(p), content, opts); 790 | } 791 | async writeFilePromise(p, content, opts) { 792 | return this.baseFs.writeFilePromise(this.fsMapToBase(p), content, opts); 793 | } 794 | writeFileSync(p, content, opts) { 795 | return this.baseFs.writeFileSync(this.fsMapToBase(p), content, opts); 796 | } 797 | async unlinkPromise(p) { 798 | return this.baseFs.unlinkPromise(this.mapToBase(p)); 799 | } 800 | unlinkSync(p) { 801 | return this.baseFs.unlinkSync(this.mapToBase(p)); 802 | } 803 | async utimesPromise(p, atime, mtime) { 804 | return this.baseFs.utimesPromise(this.mapToBase(p), atime, mtime); 805 | } 806 | utimesSync(p, atime, mtime) { 807 | return this.baseFs.utimesSync(this.mapToBase(p), atime, mtime); 808 | } 809 | async lutimesPromise(p, atime, mtime) { 810 | return this.baseFs.lutimesPromise(this.mapToBase(p), atime, mtime); 811 | } 812 | lutimesSync(p, atime, mtime) { 813 | return this.baseFs.lutimesSync(this.mapToBase(p), atime, mtime); 814 | } 815 | async mkdirPromise(p, opts) { 816 | return this.baseFs.mkdirPromise(this.mapToBase(p), opts); 817 | } 818 | mkdirSync(p, opts) { 819 | return this.baseFs.mkdirSync(this.mapToBase(p), opts); 820 | } 821 | async rmdirPromise(p, opts) { 822 | return this.baseFs.rmdirPromise(this.mapToBase(p), opts); 823 | } 824 | rmdirSync(p, opts) { 825 | return this.baseFs.rmdirSync(this.mapToBase(p), opts); 826 | } 827 | async linkPromise(existingP, newP) { 828 | return this.baseFs.linkPromise(this.mapToBase(existingP), this.mapToBase(newP)); 829 | } 830 | linkSync(existingP, newP) { 831 | return this.baseFs.linkSync(this.mapToBase(existingP), this.mapToBase(newP)); 832 | } 833 | async symlinkPromise(target, p, type) { 834 | const mappedP = this.mapToBase(p); 835 | if (this.pathUtils.isAbsolute(target)) 836 | return this.baseFs.symlinkPromise(this.mapToBase(target), mappedP, type); 837 | const mappedAbsoluteTarget = this.mapToBase(this.pathUtils.join(this.pathUtils.dirname(p), target)); 838 | const mappedTarget = this.baseFs.pathUtils.relative(this.baseFs.pathUtils.dirname(mappedP), mappedAbsoluteTarget); 839 | return this.baseFs.symlinkPromise(mappedTarget, mappedP, type); 840 | } 841 | symlinkSync(target, p, type) { 842 | const mappedP = this.mapToBase(p); 843 | if (this.pathUtils.isAbsolute(target)) 844 | return this.baseFs.symlinkSync(this.mapToBase(target), mappedP, type); 845 | const mappedAbsoluteTarget = this.mapToBase(this.pathUtils.join(this.pathUtils.dirname(p), target)); 846 | const mappedTarget = this.baseFs.pathUtils.relative(this.baseFs.pathUtils.dirname(mappedP), mappedAbsoluteTarget); 847 | return this.baseFs.symlinkSync(mappedTarget, mappedP, type); 848 | } 849 | async readFilePromise(p, encoding) { 850 | return this.baseFs.readFilePromise(this.fsMapToBase(p), encoding); 851 | } 852 | readFileSync(p, encoding) { 853 | return this.baseFs.readFileSync(this.fsMapToBase(p), encoding); 854 | } 855 | readdirPromise(p, opts) { 856 | return this.baseFs.readdirPromise(this.mapToBase(p), opts); 857 | } 858 | readdirSync(p, opts) { 859 | return this.baseFs.readdirSync(this.mapToBase(p), opts); 860 | } 861 | async readlinkPromise(p) { 862 | return this.mapFromBase(await this.baseFs.readlinkPromise(this.mapToBase(p))); 863 | } 864 | readlinkSync(p) { 865 | return this.mapFromBase(this.baseFs.readlinkSync(this.mapToBase(p))); 866 | } 867 | async truncatePromise(p, len) { 868 | return this.baseFs.truncatePromise(this.mapToBase(p), len); 869 | } 870 | truncateSync(p, len) { 871 | return this.baseFs.truncateSync(this.mapToBase(p), len); 872 | } 873 | async ftruncatePromise(fd, len) { 874 | return this.baseFs.ftruncatePromise(fd, len); 875 | } 876 | ftruncateSync(fd, len) { 877 | return this.baseFs.ftruncateSync(fd, len); 878 | } 879 | watch(p, a, b) { 880 | return this.baseFs.watch( 881 | this.mapToBase(p), 882 | a, 883 | b 884 | ); 885 | } 886 | watchFile(p, a, b) { 887 | return this.baseFs.watchFile( 888 | this.mapToBase(p), 889 | a, 890 | b 891 | ); 892 | } 893 | unwatchFile(p, cb) { 894 | return this.baseFs.unwatchFile(this.mapToBase(p), cb); 895 | } 896 | fsMapToBase(p) { 897 | if (typeof p === `number`) { 898 | return p; 899 | } else { 900 | return this.mapToBase(p); 901 | } 902 | } 903 | } 904 | 905 | class NodeFS extends BasePortableFakeFS { 906 | constructor(realFs = fs) { 907 | super(); 908 | this.realFs = realFs; 909 | } 910 | getExtractHint() { 911 | return false; 912 | } 913 | getRealPath() { 914 | return PortablePath.root; 915 | } 916 | resolve(p) { 917 | return ppath.resolve(p); 918 | } 919 | async openPromise(p, flags, mode) { 920 | return await new Promise((resolve, reject) => { 921 | this.realFs.open(npath.fromPortablePath(p), flags, mode, this.makeCallback(resolve, reject)); 922 | }); 923 | } 924 | openSync(p, flags, mode) { 925 | return this.realFs.openSync(npath.fromPortablePath(p), flags, mode); 926 | } 927 | async opendirPromise(p, opts) { 928 | return await new Promise((resolve, reject) => { 929 | if (typeof opts !== `undefined`) { 930 | this.realFs.opendir(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); 931 | } else { 932 | this.realFs.opendir(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); 933 | } 934 | }).then((dir) => { 935 | const dirWithFixedPath = dir; 936 | Object.defineProperty(dirWithFixedPath, `path`, { 937 | value: p, 938 | configurable: true, 939 | writable: true 940 | }); 941 | return dirWithFixedPath; 942 | }); 943 | } 944 | opendirSync(p, opts) { 945 | const dir = typeof opts !== `undefined` ? this.realFs.opendirSync(npath.fromPortablePath(p), opts) : this.realFs.opendirSync(npath.fromPortablePath(p)); 946 | const dirWithFixedPath = dir; 947 | Object.defineProperty(dirWithFixedPath, `path`, { 948 | value: p, 949 | configurable: true, 950 | writable: true 951 | }); 952 | return dirWithFixedPath; 953 | } 954 | async readPromise(fd, buffer, offset = 0, length = 0, position = -1) { 955 | return await new Promise((resolve, reject) => { 956 | this.realFs.read(fd, buffer, offset, length, position, (error, bytesRead) => { 957 | if (error) { 958 | reject(error); 959 | } else { 960 | resolve(bytesRead); 961 | } 962 | }); 963 | }); 964 | } 965 | readSync(fd, buffer, offset, length, position) { 966 | return this.realFs.readSync(fd, buffer, offset, length, position); 967 | } 968 | async writePromise(fd, buffer, offset, length, position) { 969 | return await new Promise((resolve, reject) => { 970 | if (typeof buffer === `string`) { 971 | return this.realFs.write(fd, buffer, offset, this.makeCallback(resolve, reject)); 972 | } else { 973 | return this.realFs.write(fd, buffer, offset, length, position, this.makeCallback(resolve, reject)); 974 | } 975 | }); 976 | } 977 | writeSync(fd, buffer, offset, length, position) { 978 | if (typeof buffer === `string`) { 979 | return this.realFs.writeSync(fd, buffer, offset); 980 | } else { 981 | return this.realFs.writeSync(fd, buffer, offset, length, position); 982 | } 983 | } 984 | async closePromise(fd) { 985 | await new Promise((resolve, reject) => { 986 | this.realFs.close(fd, this.makeCallback(resolve, reject)); 987 | }); 988 | } 989 | closeSync(fd) { 990 | this.realFs.closeSync(fd); 991 | } 992 | createReadStream(p, opts) { 993 | const realPath = p !== null ? npath.fromPortablePath(p) : p; 994 | return this.realFs.createReadStream(realPath, opts); 995 | } 996 | createWriteStream(p, opts) { 997 | const realPath = p !== null ? npath.fromPortablePath(p) : p; 998 | return this.realFs.createWriteStream(realPath, opts); 999 | } 1000 | async realpathPromise(p) { 1001 | return await new Promise((resolve, reject) => { 1002 | this.realFs.realpath(npath.fromPortablePath(p), {}, this.makeCallback(resolve, reject)); 1003 | }).then((path) => { 1004 | return npath.toPortablePath(path); 1005 | }); 1006 | } 1007 | realpathSync(p) { 1008 | return npath.toPortablePath(this.realFs.realpathSync(npath.fromPortablePath(p), {})); 1009 | } 1010 | async existsPromise(p) { 1011 | return await new Promise((resolve) => { 1012 | this.realFs.exists(npath.fromPortablePath(p), resolve); 1013 | }); 1014 | } 1015 | accessSync(p, mode) { 1016 | return this.realFs.accessSync(npath.fromPortablePath(p), mode); 1017 | } 1018 | async accessPromise(p, mode) { 1019 | return await new Promise((resolve, reject) => { 1020 | this.realFs.access(npath.fromPortablePath(p), mode, this.makeCallback(resolve, reject)); 1021 | }); 1022 | } 1023 | existsSync(p) { 1024 | return this.realFs.existsSync(npath.fromPortablePath(p)); 1025 | } 1026 | async statPromise(p, opts) { 1027 | return await new Promise((resolve, reject) => { 1028 | if (opts) { 1029 | this.realFs.stat(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); 1030 | } else { 1031 | this.realFs.stat(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); 1032 | } 1033 | }); 1034 | } 1035 | statSync(p, opts) { 1036 | if (opts) { 1037 | return this.realFs.statSync(npath.fromPortablePath(p), opts); 1038 | } else { 1039 | return this.realFs.statSync(npath.fromPortablePath(p)); 1040 | } 1041 | } 1042 | async fstatPromise(fd, opts) { 1043 | return await new Promise((resolve, reject) => { 1044 | if (opts) { 1045 | this.realFs.fstat(fd, opts, this.makeCallback(resolve, reject)); 1046 | } else { 1047 | this.realFs.fstat(fd, this.makeCallback(resolve, reject)); 1048 | } 1049 | }); 1050 | } 1051 | fstatSync(fd, opts) { 1052 | if (opts) { 1053 | return this.realFs.fstatSync(fd, opts); 1054 | } else { 1055 | return this.realFs.fstatSync(fd); 1056 | } 1057 | } 1058 | async lstatPromise(p, opts) { 1059 | return await new Promise((resolve, reject) => { 1060 | if (opts) { 1061 | this.realFs.lstat(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); 1062 | } else { 1063 | this.realFs.lstat(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); 1064 | } 1065 | }); 1066 | } 1067 | lstatSync(p, opts) { 1068 | if (opts) { 1069 | return this.realFs.lstatSync(npath.fromPortablePath(p), opts); 1070 | } else { 1071 | return this.realFs.lstatSync(npath.fromPortablePath(p)); 1072 | } 1073 | } 1074 | async fchmodPromise(fd, mask) { 1075 | return await new Promise((resolve, reject) => { 1076 | this.realFs.fchmod(fd, mask, this.makeCallback(resolve, reject)); 1077 | }); 1078 | } 1079 | fchmodSync(fd, mask) { 1080 | return this.realFs.fchmodSync(fd, mask); 1081 | } 1082 | async chmodPromise(p, mask) { 1083 | return await new Promise((resolve, reject) => { 1084 | this.realFs.chmod(npath.fromPortablePath(p), mask, this.makeCallback(resolve, reject)); 1085 | }); 1086 | } 1087 | chmodSync(p, mask) { 1088 | return this.realFs.chmodSync(npath.fromPortablePath(p), mask); 1089 | } 1090 | async fchownPromise(fd, uid, gid) { 1091 | return await new Promise((resolve, reject) => { 1092 | this.realFs.fchown(fd, uid, gid, this.makeCallback(resolve, reject)); 1093 | }); 1094 | } 1095 | fchownSync(fd, uid, gid) { 1096 | return this.realFs.fchownSync(fd, uid, gid); 1097 | } 1098 | async chownPromise(p, uid, gid) { 1099 | return await new Promise((resolve, reject) => { 1100 | this.realFs.chown(npath.fromPortablePath(p), uid, gid, this.makeCallback(resolve, reject)); 1101 | }); 1102 | } 1103 | chownSync(p, uid, gid) { 1104 | return this.realFs.chownSync(npath.fromPortablePath(p), uid, gid); 1105 | } 1106 | async renamePromise(oldP, newP) { 1107 | return await new Promise((resolve, reject) => { 1108 | this.realFs.rename(npath.fromPortablePath(oldP), npath.fromPortablePath(newP), this.makeCallback(resolve, reject)); 1109 | }); 1110 | } 1111 | renameSync(oldP, newP) { 1112 | return this.realFs.renameSync(npath.fromPortablePath(oldP), npath.fromPortablePath(newP)); 1113 | } 1114 | async copyFilePromise(sourceP, destP, flags = 0) { 1115 | return await new Promise((resolve, reject) => { 1116 | this.realFs.copyFile(npath.fromPortablePath(sourceP), npath.fromPortablePath(destP), flags, this.makeCallback(resolve, reject)); 1117 | }); 1118 | } 1119 | copyFileSync(sourceP, destP, flags = 0) { 1120 | return this.realFs.copyFileSync(npath.fromPortablePath(sourceP), npath.fromPortablePath(destP), flags); 1121 | } 1122 | async appendFilePromise(p, content, opts) { 1123 | return await new Promise((resolve, reject) => { 1124 | const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; 1125 | if (opts) { 1126 | this.realFs.appendFile(fsNativePath, content, opts, this.makeCallback(resolve, reject)); 1127 | } else { 1128 | this.realFs.appendFile(fsNativePath, content, this.makeCallback(resolve, reject)); 1129 | } 1130 | }); 1131 | } 1132 | appendFileSync(p, content, opts) { 1133 | const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; 1134 | if (opts) { 1135 | this.realFs.appendFileSync(fsNativePath, content, opts); 1136 | } else { 1137 | this.realFs.appendFileSync(fsNativePath, content); 1138 | } 1139 | } 1140 | async writeFilePromise(p, content, opts) { 1141 | return await new Promise((resolve, reject) => { 1142 | const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; 1143 | if (opts) { 1144 | this.realFs.writeFile(fsNativePath, content, opts, this.makeCallback(resolve, reject)); 1145 | } else { 1146 | this.realFs.writeFile(fsNativePath, content, this.makeCallback(resolve, reject)); 1147 | } 1148 | }); 1149 | } 1150 | writeFileSync(p, content, opts) { 1151 | const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; 1152 | if (opts) { 1153 | this.realFs.writeFileSync(fsNativePath, content, opts); 1154 | } else { 1155 | this.realFs.writeFileSync(fsNativePath, content); 1156 | } 1157 | } 1158 | async unlinkPromise(p) { 1159 | return await new Promise((resolve, reject) => { 1160 | this.realFs.unlink(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); 1161 | }); 1162 | } 1163 | unlinkSync(p) { 1164 | return this.realFs.unlinkSync(npath.fromPortablePath(p)); 1165 | } 1166 | async utimesPromise(p, atime, mtime) { 1167 | return await new Promise((resolve, reject) => { 1168 | this.realFs.utimes(npath.fromPortablePath(p), atime, mtime, this.makeCallback(resolve, reject)); 1169 | }); 1170 | } 1171 | utimesSync(p, atime, mtime) { 1172 | this.realFs.utimesSync(npath.fromPortablePath(p), atime, mtime); 1173 | } 1174 | async lutimesPromise(p, atime, mtime) { 1175 | return await new Promise((resolve, reject) => { 1176 | this.realFs.lutimes(npath.fromPortablePath(p), atime, mtime, this.makeCallback(resolve, reject)); 1177 | }); 1178 | } 1179 | lutimesSync(p, atime, mtime) { 1180 | this.realFs.lutimesSync(npath.fromPortablePath(p), atime, mtime); 1181 | } 1182 | async mkdirPromise(p, opts) { 1183 | return await new Promise((resolve, reject) => { 1184 | this.realFs.mkdir(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); 1185 | }); 1186 | } 1187 | mkdirSync(p, opts) { 1188 | return this.realFs.mkdirSync(npath.fromPortablePath(p), opts); 1189 | } 1190 | async rmdirPromise(p, opts) { 1191 | return await new Promise((resolve, reject) => { 1192 | if (opts) { 1193 | this.realFs.rmdir(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); 1194 | } else { 1195 | this.realFs.rmdir(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); 1196 | } 1197 | }); 1198 | } 1199 | rmdirSync(p, opts) { 1200 | return this.realFs.rmdirSync(npath.fromPortablePath(p), opts); 1201 | } 1202 | async linkPromise(existingP, newP) { 1203 | return await new Promise((resolve, reject) => { 1204 | this.realFs.link(npath.fromPortablePath(existingP), npath.fromPortablePath(newP), this.makeCallback(resolve, reject)); 1205 | }); 1206 | } 1207 | linkSync(existingP, newP) { 1208 | return this.realFs.linkSync(npath.fromPortablePath(existingP), npath.fromPortablePath(newP)); 1209 | } 1210 | async symlinkPromise(target, p, type) { 1211 | return await new Promise((resolve, reject) => { 1212 | this.realFs.symlink(npath.fromPortablePath(target.replace(/\/+$/, ``)), npath.fromPortablePath(p), type, this.makeCallback(resolve, reject)); 1213 | }); 1214 | } 1215 | symlinkSync(target, p, type) { 1216 | return this.realFs.symlinkSync(npath.fromPortablePath(target.replace(/\/+$/, ``)), npath.fromPortablePath(p), type); 1217 | } 1218 | async readFilePromise(p, encoding) { 1219 | return await new Promise((resolve, reject) => { 1220 | const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; 1221 | this.realFs.readFile(fsNativePath, encoding, this.makeCallback(resolve, reject)); 1222 | }); 1223 | } 1224 | readFileSync(p, encoding) { 1225 | const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; 1226 | return this.realFs.readFileSync(fsNativePath, encoding); 1227 | } 1228 | async readdirPromise(p, opts) { 1229 | return await new Promise((resolve, reject) => { 1230 | if (opts) { 1231 | this.realFs.readdir(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); 1232 | } else { 1233 | this.realFs.readdir(npath.fromPortablePath(p), this.makeCallback((value) => resolve(value), reject)); 1234 | } 1235 | }); 1236 | } 1237 | readdirSync(p, opts) { 1238 | if (opts) { 1239 | return this.realFs.readdirSync(npath.fromPortablePath(p), opts); 1240 | } else { 1241 | return this.realFs.readdirSync(npath.fromPortablePath(p)); 1242 | } 1243 | } 1244 | async readlinkPromise(p) { 1245 | return await new Promise((resolve, reject) => { 1246 | this.realFs.readlink(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); 1247 | }).then((path) => { 1248 | return npath.toPortablePath(path); 1249 | }); 1250 | } 1251 | readlinkSync(p) { 1252 | return npath.toPortablePath(this.realFs.readlinkSync(npath.fromPortablePath(p))); 1253 | } 1254 | async truncatePromise(p, len) { 1255 | return await new Promise((resolve, reject) => { 1256 | this.realFs.truncate(npath.fromPortablePath(p), len, this.makeCallback(resolve, reject)); 1257 | }); 1258 | } 1259 | truncateSync(p, len) { 1260 | return this.realFs.truncateSync(npath.fromPortablePath(p), len); 1261 | } 1262 | async ftruncatePromise(fd, len) { 1263 | return await new Promise((resolve, reject) => { 1264 | this.realFs.ftruncate(fd, len, this.makeCallback(resolve, reject)); 1265 | }); 1266 | } 1267 | ftruncateSync(fd, len) { 1268 | return this.realFs.ftruncateSync(fd, len); 1269 | } 1270 | watch(p, a, b) { 1271 | return this.realFs.watch( 1272 | npath.fromPortablePath(p), 1273 | a, 1274 | b 1275 | ); 1276 | } 1277 | watchFile(p, a, b) { 1278 | return this.realFs.watchFile( 1279 | npath.fromPortablePath(p), 1280 | a, 1281 | b 1282 | ); 1283 | } 1284 | unwatchFile(p, cb) { 1285 | return this.realFs.unwatchFile(npath.fromPortablePath(p), cb); 1286 | } 1287 | makeCallback(resolve, reject) { 1288 | return (err, result) => { 1289 | if (err) { 1290 | reject(err); 1291 | } else { 1292 | resolve(result); 1293 | } 1294 | }; 1295 | } 1296 | } 1297 | 1298 | const NUMBER_REGEXP = /^[0-9]+$/; 1299 | const VIRTUAL_REGEXP = /^(\/(?:[^/]+\/)*?(?:\$\$virtual|__virtual__))((?:\/((?:[^/]+-)?[a-f0-9]+)(?:\/([^/]+))?)?((?:\/.*)?))$/; 1300 | const VALID_COMPONENT = /^([^/]+-)?[a-f0-9]+$/; 1301 | class VirtualFS extends ProxiedFS { 1302 | constructor({ baseFs = new NodeFS() } = {}) { 1303 | super(ppath); 1304 | this.baseFs = baseFs; 1305 | } 1306 | static makeVirtualPath(base, component, to) { 1307 | if (ppath.basename(base) !== `__virtual__`) 1308 | throw new Error(`Assertion failed: Virtual folders must be named "__virtual__"`); 1309 | if (!ppath.basename(component).match(VALID_COMPONENT)) 1310 | throw new Error(`Assertion failed: Virtual components must be ended by an hexadecimal hash`); 1311 | const target = ppath.relative(ppath.dirname(base), to); 1312 | const segments = target.split(`/`); 1313 | let depth = 0; 1314 | while (depth < segments.length && segments[depth] === `..`) 1315 | depth += 1; 1316 | const finalSegments = segments.slice(depth); 1317 | const fullVirtualPath = ppath.join(base, component, String(depth), ...finalSegments); 1318 | return fullVirtualPath; 1319 | } 1320 | static resolveVirtual(p) { 1321 | const match = p.match(VIRTUAL_REGEXP); 1322 | if (!match || !match[3] && match[5]) 1323 | return p; 1324 | const target = ppath.dirname(match[1]); 1325 | if (!match[3] || !match[4]) 1326 | return target; 1327 | const isnum = NUMBER_REGEXP.test(match[4]); 1328 | if (!isnum) 1329 | return p; 1330 | const depth = Number(match[4]); 1331 | const backstep = `../`.repeat(depth); 1332 | const subpath = match[5] || `.`; 1333 | return VirtualFS.resolveVirtual(ppath.join(target, backstep, subpath)); 1334 | } 1335 | getExtractHint(hints) { 1336 | return this.baseFs.getExtractHint(hints); 1337 | } 1338 | getRealPath() { 1339 | return this.baseFs.getRealPath(); 1340 | } 1341 | realpathSync(p) { 1342 | const match = p.match(VIRTUAL_REGEXP); 1343 | if (!match) 1344 | return this.baseFs.realpathSync(p); 1345 | if (!match[5]) 1346 | return p; 1347 | const realpath = this.baseFs.realpathSync(this.mapToBase(p)); 1348 | return VirtualFS.makeVirtualPath(match[1], match[3], realpath); 1349 | } 1350 | async realpathPromise(p) { 1351 | const match = p.match(VIRTUAL_REGEXP); 1352 | if (!match) 1353 | return await this.baseFs.realpathPromise(p); 1354 | if (!match[5]) 1355 | return p; 1356 | const realpath = await this.baseFs.realpathPromise(this.mapToBase(p)); 1357 | return VirtualFS.makeVirtualPath(match[1], match[3], realpath); 1358 | } 1359 | mapToBase(p) { 1360 | if (p === ``) 1361 | return p; 1362 | if (this.pathUtils.isAbsolute(p)) 1363 | return VirtualFS.resolveVirtual(p); 1364 | const resolvedRoot = VirtualFS.resolveVirtual(this.baseFs.resolve(PortablePath.dot)); 1365 | const resolvedP = VirtualFS.resolveVirtual(this.baseFs.resolve(p)); 1366 | return ppath.relative(resolvedRoot, resolvedP) || PortablePath.dot; 1367 | } 1368 | mapFromBase(p) { 1369 | return p; 1370 | } 1371 | } 1372 | 1373 | const [major, minor] = process.versions.node.split(`.`).map((value) => parseInt(value, 10)); 1374 | const WATCH_MODE_MESSAGE_USES_ARRAYS = major > 19 || major === 19 && minor >= 2 || major === 18 && minor >= 13; 1375 | const HAS_LAZY_LOADED_TRANSLATORS = major > 19 || major === 19 && minor >= 3; 1376 | 1377 | function readPackageScope(checkPath) { 1378 | const rootSeparatorIndex = checkPath.indexOf(npath.sep); 1379 | let separatorIndex; 1380 | do { 1381 | separatorIndex = checkPath.lastIndexOf(npath.sep); 1382 | checkPath = checkPath.slice(0, separatorIndex); 1383 | if (checkPath.endsWith(`${npath.sep}node_modules`)) 1384 | return false; 1385 | const pjson = readPackage(checkPath + npath.sep); 1386 | if (pjson) { 1387 | return { 1388 | data: pjson, 1389 | path: checkPath 1390 | }; 1391 | } 1392 | } while (separatorIndex > rootSeparatorIndex); 1393 | return false; 1394 | } 1395 | function readPackage(requestPath) { 1396 | const jsonPath = npath.resolve(requestPath, `package.json`); 1397 | if (!fs.existsSync(jsonPath)) 1398 | return null; 1399 | return JSON.parse(fs.readFileSync(jsonPath, `utf8`)); 1400 | } 1401 | 1402 | async function tryReadFile$1(path2) { 1403 | try { 1404 | return await fs.promises.readFile(path2, `utf8`); 1405 | } catch (error) { 1406 | if (error.code === `ENOENT`) 1407 | return null; 1408 | throw error; 1409 | } 1410 | } 1411 | function tryParseURL(str, base) { 1412 | try { 1413 | return new URL$1(str, base); 1414 | } catch { 1415 | return null; 1416 | } 1417 | } 1418 | let entrypointPath = null; 1419 | function setEntrypointPath(file) { 1420 | entrypointPath = file; 1421 | } 1422 | function getFileFormat(filepath) { 1423 | const ext = path.extname(filepath); 1424 | switch (ext) { 1425 | case `.mjs`: { 1426 | return `module`; 1427 | } 1428 | case `.cjs`: { 1429 | return `commonjs`; 1430 | } 1431 | case `.wasm`: { 1432 | throw new Error( 1433 | `Unknown file extension ".wasm" for ${filepath}` 1434 | ); 1435 | } 1436 | case `.json`: { 1437 | return `json`; 1438 | } 1439 | case `.js`: { 1440 | const pkg = readPackageScope(filepath); 1441 | if (!pkg) 1442 | return `commonjs`; 1443 | return pkg.data.type ?? `commonjs`; 1444 | } 1445 | default: { 1446 | if (entrypointPath !== filepath) 1447 | return null; 1448 | const pkg = readPackageScope(filepath); 1449 | if (!pkg) 1450 | return `commonjs`; 1451 | if (pkg.data.type === `module`) 1452 | return null; 1453 | return pkg.data.type ?? `commonjs`; 1454 | } 1455 | } 1456 | } 1457 | 1458 | async function load$1(urlString, context, nextLoad) { 1459 | const url = tryParseURL(urlString); 1460 | if (url?.protocol !== `file:`) 1461 | return nextLoad(urlString, context, nextLoad); 1462 | const filePath = fileURLToPath(url); 1463 | const format = getFileFormat(filePath); 1464 | if (!format) 1465 | return nextLoad(urlString, context, nextLoad); 1466 | if (format === `json` && context.importAssertions?.type !== `json`) { 1467 | const err = new TypeError(`[ERR_IMPORT_ASSERTION_TYPE_MISSING]: Module "${urlString}" needs an import assertion of type "json"`); 1468 | err.code = `ERR_IMPORT_ASSERTION_TYPE_MISSING`; 1469 | throw err; 1470 | } 1471 | if (process.env.WATCH_REPORT_DEPENDENCIES && process.send) { 1472 | const pathToSend = pathToFileURL( 1473 | npath.fromPortablePath( 1474 | VirtualFS.resolveVirtual(npath.toPortablePath(filePath)) 1475 | ) 1476 | ).href; 1477 | process.send({ 1478 | "watch:import": WATCH_MODE_MESSAGE_USES_ARRAYS ? [pathToSend] : pathToSend 1479 | }); 1480 | } 1481 | return { 1482 | format, 1483 | source: format === `commonjs` ? void 0 : await fs.promises.readFile(filePath, `utf8`), 1484 | shortCircuit: true 1485 | }; 1486 | } 1487 | 1488 | const ArrayIsArray = Array.isArray; 1489 | const JSONStringify = JSON.stringify; 1490 | const ObjectGetOwnPropertyNames = Object.getOwnPropertyNames; 1491 | const ObjectPrototypeHasOwnProperty = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop); 1492 | const RegExpPrototypeExec = (obj, string) => RegExp.prototype.exec.call(obj, string); 1493 | const RegExpPrototypeSymbolReplace = (obj, ...rest) => RegExp.prototype[Symbol.replace].apply(obj, rest); 1494 | const StringPrototypeEndsWith = (str, ...rest) => String.prototype.endsWith.apply(str, rest); 1495 | const StringPrototypeIncludes = (str, ...rest) => String.prototype.includes.apply(str, rest); 1496 | const StringPrototypeLastIndexOf = (str, ...rest) => String.prototype.lastIndexOf.apply(str, rest); 1497 | const StringPrototypeIndexOf = (str, ...rest) => String.prototype.indexOf.apply(str, rest); 1498 | const StringPrototypeReplace = (str, ...rest) => String.prototype.replace.apply(str, rest); 1499 | const StringPrototypeSlice = (str, ...rest) => String.prototype.slice.apply(str, rest); 1500 | const StringPrototypeStartsWith = (str, ...rest) => String.prototype.startsWith.apply(str, rest); 1501 | const SafeMap = Map; 1502 | const JSONParse = JSON.parse; 1503 | 1504 | function createErrorType(code, messageCreator, errorType) { 1505 | return class extends errorType { 1506 | constructor(...args) { 1507 | super(messageCreator(...args)); 1508 | this.code = code; 1509 | this.name = `${errorType.name} [${code}]`; 1510 | } 1511 | }; 1512 | } 1513 | const ERR_PACKAGE_IMPORT_NOT_DEFINED = createErrorType( 1514 | `ERR_PACKAGE_IMPORT_NOT_DEFINED`, 1515 | (specifier, packagePath, base) => { 1516 | return `Package import specifier "${specifier}" is not defined${packagePath ? ` in package ${packagePath}package.json` : ``} imported from ${base}`; 1517 | }, 1518 | TypeError 1519 | ); 1520 | const ERR_INVALID_MODULE_SPECIFIER = createErrorType( 1521 | `ERR_INVALID_MODULE_SPECIFIER`, 1522 | (request, reason, base = void 0) => { 1523 | return `Invalid module "${request}" ${reason}${base ? ` imported from ${base}` : ``}`; 1524 | }, 1525 | TypeError 1526 | ); 1527 | const ERR_INVALID_PACKAGE_TARGET = createErrorType( 1528 | `ERR_INVALID_PACKAGE_TARGET`, 1529 | (pkgPath, key, target, isImport = false, base = void 0) => { 1530 | const relError = typeof target === `string` && !isImport && target.length && !StringPrototypeStartsWith(target, `./`); 1531 | if (key === `.`) { 1532 | assert(isImport === false); 1533 | return `Invalid "exports" main target ${JSONStringify(target)} defined in the package config ${pkgPath}package.json${base ? ` imported from ${base}` : ``}${relError ? `; targets must start with "./"` : ``}`; 1534 | } 1535 | return `Invalid "${isImport ? `imports` : `exports`}" target ${JSONStringify( 1536 | target 1537 | )} defined for '${key}' in the package config ${pkgPath}package.json${base ? ` imported from ${base}` : ``}${relError ? `; targets must start with "./"` : ``}`; 1538 | }, 1539 | Error 1540 | ); 1541 | const ERR_INVALID_PACKAGE_CONFIG = createErrorType( 1542 | `ERR_INVALID_PACKAGE_CONFIG`, 1543 | (path, base, message) => { 1544 | return `Invalid package config ${path}${base ? ` while importing ${base}` : ``}${message ? `. ${message}` : ``}`; 1545 | }, 1546 | Error 1547 | ); 1548 | 1549 | function filterOwnProperties(source, keys) { 1550 | const filtered = /* @__PURE__ */ Object.create(null); 1551 | for (let i = 0; i < keys.length; i++) { 1552 | const key = keys[i]; 1553 | if (ObjectPrototypeHasOwnProperty(source, key)) { 1554 | filtered[key] = source[key]; 1555 | } 1556 | } 1557 | return filtered; 1558 | } 1559 | 1560 | const packageJSONCache = new SafeMap(); 1561 | function getPackageConfig(path, specifier, base, readFileSyncFn) { 1562 | const existing = packageJSONCache.get(path); 1563 | if (existing !== void 0) { 1564 | return existing; 1565 | } 1566 | const source = readFileSyncFn(path); 1567 | if (source === void 0) { 1568 | const packageConfig2 = { 1569 | pjsonPath: path, 1570 | exists: false, 1571 | main: void 0, 1572 | name: void 0, 1573 | type: "none", 1574 | exports: void 0, 1575 | imports: void 0 1576 | }; 1577 | packageJSONCache.set(path, packageConfig2); 1578 | return packageConfig2; 1579 | } 1580 | let packageJSON; 1581 | try { 1582 | packageJSON = JSONParse(source); 1583 | } catch (error) { 1584 | throw new ERR_INVALID_PACKAGE_CONFIG( 1585 | path, 1586 | (base ? `"${specifier}" from ` : "") + fileURLToPath(base || specifier), 1587 | error.message 1588 | ); 1589 | } 1590 | let { imports, main, name, type } = filterOwnProperties(packageJSON, [ 1591 | "imports", 1592 | "main", 1593 | "name", 1594 | "type" 1595 | ]); 1596 | const exports = ObjectPrototypeHasOwnProperty(packageJSON, "exports") ? packageJSON.exports : void 0; 1597 | if (typeof imports !== "object" || imports === null) { 1598 | imports = void 0; 1599 | } 1600 | if (typeof main !== "string") { 1601 | main = void 0; 1602 | } 1603 | if (typeof name !== "string") { 1604 | name = void 0; 1605 | } 1606 | if (type !== "module" && type !== "commonjs") { 1607 | type = "none"; 1608 | } 1609 | const packageConfig = { 1610 | pjsonPath: path, 1611 | exists: true, 1612 | main, 1613 | name, 1614 | type, 1615 | exports, 1616 | imports 1617 | }; 1618 | packageJSONCache.set(path, packageConfig); 1619 | return packageConfig; 1620 | } 1621 | function getPackageScopeConfig(resolved, readFileSyncFn) { 1622 | let packageJSONUrl = new URL("./package.json", resolved); 1623 | while (true) { 1624 | const packageJSONPath2 = packageJSONUrl.pathname; 1625 | if (StringPrototypeEndsWith(packageJSONPath2, "node_modules/package.json")) { 1626 | break; 1627 | } 1628 | const packageConfig2 = getPackageConfig( 1629 | fileURLToPath(packageJSONUrl), 1630 | resolved, 1631 | void 0, 1632 | readFileSyncFn 1633 | ); 1634 | if (packageConfig2.exists) { 1635 | return packageConfig2; 1636 | } 1637 | const lastPackageJSONUrl = packageJSONUrl; 1638 | packageJSONUrl = new URL("../package.json", packageJSONUrl); 1639 | if (packageJSONUrl.pathname === lastPackageJSONUrl.pathname) { 1640 | break; 1641 | } 1642 | } 1643 | const packageJSONPath = fileURLToPath(packageJSONUrl); 1644 | const packageConfig = { 1645 | pjsonPath: packageJSONPath, 1646 | exists: false, 1647 | main: void 0, 1648 | name: void 0, 1649 | type: "none", 1650 | exports: void 0, 1651 | imports: void 0 1652 | }; 1653 | packageJSONCache.set(packageJSONPath, packageConfig); 1654 | return packageConfig; 1655 | } 1656 | 1657 | /** 1658 | @license 1659 | Copyright Node.js contributors. All rights reserved. 1660 | 1661 | Permission is hereby granted, free of charge, to any person obtaining a copy 1662 | of this software and associated documentation files (the "Software"), to 1663 | deal in the Software without restriction, including without limitation the 1664 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 1665 | sell copies of the Software, and to permit persons to whom the Software is 1666 | furnished to do so, subject to the following conditions: 1667 | 1668 | The above copyright notice and this permission notice shall be included in 1669 | all copies or substantial portions of the Software. 1670 | 1671 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1672 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1673 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1674 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1675 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 1676 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 1677 | IN THE SOFTWARE. 1678 | */ 1679 | function throwImportNotDefined(specifier, packageJSONUrl, base) { 1680 | throw new ERR_PACKAGE_IMPORT_NOT_DEFINED( 1681 | specifier, 1682 | packageJSONUrl && fileURLToPath(new URL(".", packageJSONUrl)), 1683 | fileURLToPath(base) 1684 | ); 1685 | } 1686 | function throwInvalidSubpath(subpath, packageJSONUrl, internal, base) { 1687 | const reason = `request is not a valid subpath for the "${internal ? "imports" : "exports"}" resolution of ${fileURLToPath(packageJSONUrl)}`; 1688 | throw new ERR_INVALID_MODULE_SPECIFIER( 1689 | subpath, 1690 | reason, 1691 | base && fileURLToPath(base) 1692 | ); 1693 | } 1694 | function throwInvalidPackageTarget(subpath, target, packageJSONUrl, internal, base) { 1695 | if (typeof target === "object" && target !== null) { 1696 | target = JSONStringify(target, null, ""); 1697 | } else { 1698 | target = `${target}`; 1699 | } 1700 | throw new ERR_INVALID_PACKAGE_TARGET( 1701 | fileURLToPath(new URL(".", packageJSONUrl)), 1702 | subpath, 1703 | target, 1704 | internal, 1705 | base && fileURLToPath(base) 1706 | ); 1707 | } 1708 | const invalidSegmentRegEx = /(^|\\|\/)((\.|%2e)(\.|%2e)?|(n|%6e|%4e)(o|%6f|%4f)(d|%64|%44)(e|%65|%45)(_|%5f)(m|%6d|%4d)(o|%6f|%4f)(d|%64|%44)(u|%75|%55)(l|%6c|%4c)(e|%65|%45)(s|%73|%53))(\\|\/|$)/i; 1709 | const patternRegEx = /\*/g; 1710 | function resolvePackageTargetString(target, subpath, match, packageJSONUrl, base, pattern, internal, conditions) { 1711 | if (subpath !== "" && !pattern && target[target.length - 1] !== "/") 1712 | throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); 1713 | if (!StringPrototypeStartsWith(target, "./")) { 1714 | if (internal && !StringPrototypeStartsWith(target, "../") && !StringPrototypeStartsWith(target, "/")) { 1715 | let isURL = false; 1716 | try { 1717 | new URL(target); 1718 | isURL = true; 1719 | } catch { 1720 | } 1721 | if (!isURL) { 1722 | const exportTarget = pattern ? RegExpPrototypeSymbolReplace(patternRegEx, target, () => subpath) : target + subpath; 1723 | return exportTarget; 1724 | } 1725 | } 1726 | throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); 1727 | } 1728 | if (RegExpPrototypeExec( 1729 | invalidSegmentRegEx, 1730 | StringPrototypeSlice(target, 2) 1731 | ) !== null) 1732 | throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); 1733 | const resolved = new URL(target, packageJSONUrl); 1734 | const resolvedPath = resolved.pathname; 1735 | const packagePath = new URL(".", packageJSONUrl).pathname; 1736 | if (!StringPrototypeStartsWith(resolvedPath, packagePath)) 1737 | throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); 1738 | if (subpath === "") 1739 | return resolved; 1740 | if (RegExpPrototypeExec(invalidSegmentRegEx, subpath) !== null) { 1741 | const request = pattern ? StringPrototypeReplace(match, "*", () => subpath) : match + subpath; 1742 | throwInvalidSubpath(request, packageJSONUrl, internal, base); 1743 | } 1744 | if (pattern) { 1745 | return new URL( 1746 | RegExpPrototypeSymbolReplace(patternRegEx, resolved.href, () => subpath) 1747 | ); 1748 | } 1749 | return new URL(subpath, resolved); 1750 | } 1751 | function isArrayIndex(key) { 1752 | const keyNum = +key; 1753 | if (`${keyNum}` !== key) 1754 | return false; 1755 | return keyNum >= 0 && keyNum < 4294967295; 1756 | } 1757 | function resolvePackageTarget(packageJSONUrl, target, subpath, packageSubpath, base, pattern, internal, conditions) { 1758 | if (typeof target === "string") { 1759 | return resolvePackageTargetString( 1760 | target, 1761 | subpath, 1762 | packageSubpath, 1763 | packageJSONUrl, 1764 | base, 1765 | pattern, 1766 | internal); 1767 | } else if (ArrayIsArray(target)) { 1768 | if (target.length === 0) { 1769 | return null; 1770 | } 1771 | let lastException; 1772 | for (let i = 0; i < target.length; i++) { 1773 | const targetItem = target[i]; 1774 | let resolveResult; 1775 | try { 1776 | resolveResult = resolvePackageTarget( 1777 | packageJSONUrl, 1778 | targetItem, 1779 | subpath, 1780 | packageSubpath, 1781 | base, 1782 | pattern, 1783 | internal, 1784 | conditions 1785 | ); 1786 | } catch (e) { 1787 | lastException = e; 1788 | if (e.code === "ERR_INVALID_PACKAGE_TARGET") { 1789 | continue; 1790 | } 1791 | throw e; 1792 | } 1793 | if (resolveResult === void 0) { 1794 | continue; 1795 | } 1796 | if (resolveResult === null) { 1797 | lastException = null; 1798 | continue; 1799 | } 1800 | return resolveResult; 1801 | } 1802 | if (lastException === void 0 || lastException === null) 1803 | return lastException; 1804 | throw lastException; 1805 | } else if (typeof target === "object" && target !== null) { 1806 | const keys = ObjectGetOwnPropertyNames(target); 1807 | for (let i = 0; i < keys.length; i++) { 1808 | const key = keys[i]; 1809 | if (isArrayIndex(key)) { 1810 | throw new ERR_INVALID_PACKAGE_CONFIG( 1811 | fileURLToPath(packageJSONUrl), 1812 | base, 1813 | '"exports" cannot contain numeric property keys.' 1814 | ); 1815 | } 1816 | } 1817 | for (let i = 0; i < keys.length; i++) { 1818 | const key = keys[i]; 1819 | if (key === "default" || conditions.has(key)) { 1820 | const conditionalTarget = target[key]; 1821 | const resolveResult = resolvePackageTarget( 1822 | packageJSONUrl, 1823 | conditionalTarget, 1824 | subpath, 1825 | packageSubpath, 1826 | base, 1827 | pattern, 1828 | internal, 1829 | conditions 1830 | ); 1831 | if (resolveResult === void 0) 1832 | continue; 1833 | return resolveResult; 1834 | } 1835 | } 1836 | return void 0; 1837 | } else if (target === null) { 1838 | return null; 1839 | } 1840 | throwInvalidPackageTarget( 1841 | packageSubpath, 1842 | target, 1843 | packageJSONUrl, 1844 | internal, 1845 | base 1846 | ); 1847 | } 1848 | function patternKeyCompare(a, b) { 1849 | const aPatternIndex = StringPrototypeIndexOf(a, "*"); 1850 | const bPatternIndex = StringPrototypeIndexOf(b, "*"); 1851 | const baseLenA = aPatternIndex === -1 ? a.length : aPatternIndex + 1; 1852 | const baseLenB = bPatternIndex === -1 ? b.length : bPatternIndex + 1; 1853 | if (baseLenA > baseLenB) 1854 | return -1; 1855 | if (baseLenB > baseLenA) 1856 | return 1; 1857 | if (aPatternIndex === -1) 1858 | return 1; 1859 | if (bPatternIndex === -1) 1860 | return -1; 1861 | if (a.length > b.length) 1862 | return -1; 1863 | if (b.length > a.length) 1864 | return 1; 1865 | return 0; 1866 | } 1867 | function packageImportsResolve({ name, base, conditions, readFileSyncFn }) { 1868 | if (name === "#" || StringPrototypeStartsWith(name, "#/") || StringPrototypeEndsWith(name, "/")) { 1869 | const reason = "is not a valid internal imports specifier name"; 1870 | throw new ERR_INVALID_MODULE_SPECIFIER(name, reason, fileURLToPath(base)); 1871 | } 1872 | let packageJSONUrl; 1873 | const packageConfig = getPackageScopeConfig(base, readFileSyncFn); 1874 | if (packageConfig.exists) { 1875 | packageJSONUrl = pathToFileURL(packageConfig.pjsonPath); 1876 | const imports = packageConfig.imports; 1877 | if (imports) { 1878 | if (ObjectPrototypeHasOwnProperty(imports, name) && !StringPrototypeIncludes(name, "*")) { 1879 | const resolveResult = resolvePackageTarget( 1880 | packageJSONUrl, 1881 | imports[name], 1882 | "", 1883 | name, 1884 | base, 1885 | false, 1886 | true, 1887 | conditions 1888 | ); 1889 | if (resolveResult != null) { 1890 | return resolveResult; 1891 | } 1892 | } else { 1893 | let bestMatch = ""; 1894 | let bestMatchSubpath; 1895 | const keys = ObjectGetOwnPropertyNames(imports); 1896 | for (let i = 0; i < keys.length; i++) { 1897 | const key = keys[i]; 1898 | const patternIndex = StringPrototypeIndexOf(key, "*"); 1899 | if (patternIndex !== -1 && StringPrototypeStartsWith( 1900 | name, 1901 | StringPrototypeSlice(key, 0, patternIndex) 1902 | )) { 1903 | const patternTrailer = StringPrototypeSlice(key, patternIndex + 1); 1904 | if (name.length >= key.length && StringPrototypeEndsWith(name, patternTrailer) && patternKeyCompare(bestMatch, key) === 1 && StringPrototypeLastIndexOf(key, "*") === patternIndex) { 1905 | bestMatch = key; 1906 | bestMatchSubpath = StringPrototypeSlice( 1907 | name, 1908 | patternIndex, 1909 | name.length - patternTrailer.length 1910 | ); 1911 | } 1912 | } 1913 | } 1914 | if (bestMatch) { 1915 | const target = imports[bestMatch]; 1916 | const resolveResult = resolvePackageTarget( 1917 | packageJSONUrl, 1918 | target, 1919 | bestMatchSubpath, 1920 | bestMatch, 1921 | base, 1922 | true, 1923 | true, 1924 | conditions 1925 | ); 1926 | if (resolveResult != null) { 1927 | return resolveResult; 1928 | } 1929 | } 1930 | } 1931 | } 1932 | } 1933 | throwImportNotDefined(name, packageJSONUrl, base); 1934 | } 1935 | 1936 | const pathRegExp = /^(?![a-zA-Z]:[\\/]|\\\\|\.{0,2}(?:\/|$))((?:node:)?(?:@[^/]+\/)?[^/]+)\/*(.*|)$/; 1937 | const isRelativeRegexp = /^\.{0,2}\//; 1938 | function tryReadFile(filePath) { 1939 | try { 1940 | return fs.readFileSync(filePath, `utf8`); 1941 | } catch (err) { 1942 | if (err.code === `ENOENT`) 1943 | return void 0; 1944 | throw err; 1945 | } 1946 | } 1947 | async function resolvePrivateRequest(specifier, issuer, context, nextResolve) { 1948 | const resolved = packageImportsResolve({ 1949 | name: specifier, 1950 | base: pathToFileURL(issuer), 1951 | conditions: new Set(context.conditions), 1952 | readFileSyncFn: tryReadFile 1953 | }); 1954 | if (resolved instanceof URL) { 1955 | return { url: resolved.href, shortCircuit: true }; 1956 | } else { 1957 | if (resolved.startsWith(`#`)) 1958 | throw new Error(`Mapping from one private import to another isn't allowed`); 1959 | return resolve$1(resolved, context, nextResolve); 1960 | } 1961 | } 1962 | async function resolve$1(originalSpecifier, context, nextResolve) { 1963 | const { findPnpApi } = moduleExports; 1964 | if (!findPnpApi || isBuiltin(originalSpecifier)) 1965 | return nextResolve(originalSpecifier, context, nextResolve); 1966 | let specifier = originalSpecifier; 1967 | const url = tryParseURL(specifier, isRelativeRegexp.test(specifier) ? context.parentURL : void 0); 1968 | if (url) { 1969 | if (url.protocol !== `file:`) 1970 | return nextResolve(originalSpecifier, context, nextResolve); 1971 | specifier = fileURLToPath(url); 1972 | } 1973 | const { parentURL, conditions = [] } = context; 1974 | const issuer = parentURL && tryParseURL(parentURL)?.protocol === `file:` ? fileURLToPath(parentURL) : process.cwd(); 1975 | const pnpapi = findPnpApi(issuer) ?? (url ? findPnpApi(specifier) : null); 1976 | if (!pnpapi) 1977 | return nextResolve(originalSpecifier, context, nextResolve); 1978 | if (specifier.startsWith(`#`)) 1979 | return resolvePrivateRequest(specifier, issuer, context, nextResolve); 1980 | const dependencyNameMatch = specifier.match(pathRegExp); 1981 | let allowLegacyResolve = false; 1982 | if (dependencyNameMatch) { 1983 | const [, dependencyName, subPath] = dependencyNameMatch; 1984 | if (subPath === `` && dependencyName !== `pnpapi`) { 1985 | const resolved = pnpapi.resolveToUnqualified(`${dependencyName}/package.json`, issuer); 1986 | if (resolved) { 1987 | const content = await tryReadFile$1(resolved); 1988 | if (content) { 1989 | const pkg = JSON.parse(content); 1990 | allowLegacyResolve = pkg.exports == null; 1991 | } 1992 | } 1993 | } 1994 | } 1995 | let result; 1996 | try { 1997 | result = pnpapi.resolveRequest(specifier, issuer, { 1998 | conditions: new Set(conditions), 1999 | extensions: allowLegacyResolve ? void 0 : [] 2000 | }); 2001 | } catch (err) { 2002 | if (err instanceof Error && `code` in err && err.code === `MODULE_NOT_FOUND`) 2003 | err.code = `ERR_MODULE_NOT_FOUND`; 2004 | throw err; 2005 | } 2006 | if (!result) 2007 | throw new Error(`Resolving '${specifier}' from '${issuer}' failed`); 2008 | const resultURL = pathToFileURL(result); 2009 | if (url) { 2010 | resultURL.search = url.search; 2011 | resultURL.hash = url.hash; 2012 | } 2013 | if (!parentURL) 2014 | setEntrypointPath(fileURLToPath(resultURL)); 2015 | return { 2016 | url: resultURL.href, 2017 | shortCircuit: true 2018 | }; 2019 | } 2020 | 2021 | if (!HAS_LAZY_LOADED_TRANSLATORS) { 2022 | const binding = process.binding(`fs`); 2023 | const originalfstat = binding.fstat; 2024 | const ZIP_MASK = 4278190080; 2025 | const ZIP_MAGIC = 704643072; 2026 | binding.fstat = function(...args) { 2027 | const [fd, useBigint, req] = args; 2028 | if ((fd & ZIP_MASK) === ZIP_MAGIC && useBigint === false && req === void 0) { 2029 | try { 2030 | const stats = fs.fstatSync(fd); 2031 | return new Float64Array([ 2032 | stats.dev, 2033 | stats.mode, 2034 | stats.nlink, 2035 | stats.uid, 2036 | stats.gid, 2037 | stats.rdev, 2038 | stats.blksize, 2039 | stats.ino, 2040 | stats.size, 2041 | stats.blocks 2042 | ]); 2043 | } catch { 2044 | } 2045 | } 2046 | return originalfstat.apply(this, args); 2047 | }; 2048 | } 2049 | 2050 | const resolve = resolve$1; 2051 | const load = load$1; 2052 | 2053 | export { load, resolve }; 2054 | -------------------------------------------------------------------------------- /.yarn/install-state.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codesandbox/excalidraw-firebase/65098d9cd5135a247f3ce4471fc37987f1f03875/.yarn/install-state.gz -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | yarnPath: .yarn/releases/yarn-4.0.1.cjs 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 CodeSandbox 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # excalidraw-firebase 2 | 3 | > A persistent catalogue of your and/or company Excalidraws 4 | 5 |  6 | 7 | ## How to set up 8 | 9 | 1. Create a Firebase Project on the [Firebase Console](https://console.firebase.google.com/u/0/) 10 | 2. Copy the Firebase config to the `src/firebase.config.json` file: 11 | 12 | ```json 13 | { 14 | "apiKey": "...", 15 | "authDomain": "...", 16 | "projectId": "...", 17 | "storageBucket": "...", 18 | "messagingSenderId": "...", 19 | "appId": "..." 20 | } 21 | ``` 22 | 23 | 3. Add **Google** as Authentication -> Sign In Method, in Firebase Console 24 | 4. Install the Firebase tools: `yarn add -g firebase-tools` and log in `firebase login` 25 | 5. Change the `firestore.rules` file to reflect your personal email or your company Google domain 26 | 6. (Optional) Go to Authentication -> Sign In Method and add a custom domain 27 | 7. Deploy it with `yarn deploy` 28 | -------------------------------------------------------------------------------- /__mocks__/@excalidraw/excalidraw.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /__mocks__/points-on-curve.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /__mocks__/roughjs/bin/math.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Random: class { 3 | next() { 4 | return 0; 5 | } 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /__mocks__/roughjs/bin/rough.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "firestore": { 3 | "rules": "firestore.rules", 4 | "indexes": "firestore.indexes.json" 5 | }, 6 | "hosting": { 7 | "public": "dist", 8 | "ignore": ["firebase.json", "**/.*", "**/node_modules/**"], 9 | "rewrites": [ 10 | { 11 | "source": "**", 12 | "destination": "/index.html" 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /firestore.indexes.json: -------------------------------------------------------------------------------- 1 | { 2 | "indexes": [], 3 | "fieldOverrides": [] 4 | } 5 | -------------------------------------------------------------------------------- /firestore.rules: -------------------------------------------------------------------------------- 1 | rules_version = '2'; 2 | service cloud.firestore { 3 | match /databases/{database}/documents { 4 | match /{document=**} { 5 | allow read, write: if request.auth.token.email.matches('.*@codesandbox.io$'); 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |You're seeing this because you've successfully setup Firebase Hosting. Now it's time to go build something extraordinary!
46 | Open Hosting Documentation 47 |Firebase SDK Loading…
49 | 50 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codesandbox/excalidraw-firebase/65098d9cd5135a247f3ce4471fc37987f1f03875/screenshot.png -------------------------------------------------------------------------------- /src/environment-interface/authentication.ts: -------------------------------------------------------------------------------- 1 | import { TEmit, TSubscribe } from "react-states"; 2 | 3 | export type User = { 4 | uid: string; 5 | name: string; 6 | avatarUrl: string | null; 7 | }; 8 | 9 | export type AuthenticationEvent = 10 | | { 11 | type: "AUTHENTICATION:AUTHENTICATED"; 12 | user: User; 13 | loomApiKey: string | null; 14 | } 15 | | { 16 | type: "AUTHENTICATION:UNAUTHENTICATED"; 17 | } 18 | | { 19 | type: "AUTHENTICATION:SIGN_IN_ERROR"; 20 | error: string; 21 | }; 22 | 23 | export interface Authentication { 24 | subscribe: TSubscribeThere was an error: {error}
18 | ), 19 | PREVIEWS_LOADED: ({ excalidraws }) => ( 20 |There was an error: {error}
32 | ), 33 | PREVIEWS_LOADED: ({ excalidraws }) => ( 34 |