more cleanup
This commit is contained in:
parent
ffe9ca2bb5
commit
3a89a076d9
|
@ -4,14 +4,10 @@ pnpm for workspaces.
|
|||
|
||||
Kubernetes for Development using Tiltfile
|
||||
|
||||
kubefwd and entr for DNS in dev cluster
|
||||
|
||||
dokku for Production, deployed with `git push`.
|
||||
|
||||
(dokku is slowly being replaced by Kubernetes)
|
||||
|
||||
Kubernetes for Production, deployed using FluxCD
|
||||
|
||||
Tested on VKE v1.30.0+1 (PVCs on other versions may not be fulfilled)
|
||||
|
||||
direnv for loading .envrc
|
||||
|
||||
Temporal for work queue
|
||||
|
@ -21,6 +17,5 @@ Postgres for data storage
|
|||
S3 for media storage
|
||||
|
||||
Domain Driven Development
|
||||
Test Driven Development
|
||||
|
||||
Tested on VKE v1.30.0+1 (PVCs on other versions may not be fulfilled)
|
||||
Test Driven Development
|
||||
|
|
20
README.md
20
README.md
|
@ -4,3 +4,23 @@ Source Code for https://futureporn.net
|
|||
|
||||
See ./ARCHITECTURE.md for overview
|
||||
|
||||
## Development Mantras
|
||||
|
||||
### Move fast and break things
|
||||
|
||||
### Make it work, make it right, make it fast (in that order)
|
||||
|
||||
### Done is better than perfect
|
||||
|
||||
### If it looks like a duck and quacks like a duck, it is a duck.
|
||||
|
||||
### If the way is long, the way is wrong
|
||||
|
||||
### Good, Fast, Cheap. Pick only two.
|
||||
|
||||
### Organizations are fractals
|
||||
|
||||
### Focus on what moves the needle
|
||||
|
||||
### Alligator energy
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Futureporn node packages
|
||||
|
||||
Each folder here is an individual node package
|
||||
Each folder here is an individual node package, each of which can reference each other.
|
||||
|
||||
See https://pnpm.io/workspaces
|
|
@ -0,0 +1,3 @@
|
|||
# bot
|
||||
|
||||
A.K.A. FutureButt, the discord bot that integrates into FP backend.
|
|
@ -0,0 +1,3 @@
|
|||
# infra
|
||||
|
||||
This module contains scripts that help with miscellaneous infrastructure tasks.
|
|
@ -1,3 +0,0 @@
|
|||
node_modules
|
||||
.env
|
||||
*~
|
|
@ -1,3 +0,0 @@
|
|||
PORT=3030
|
||||
IPFS_URL=http://localhost:5001
|
||||
API_KEY=changeme
|
|
@ -1,144 +0,0 @@
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/node
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
### Node Patch ###
|
||||
# Serverless Webpack directories
|
||||
.webpack/
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
# SvelteKit build / generate output
|
||||
.svelte-kit
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node
|
|
@ -1,3 +0,0 @@
|
|||
engine-strict=true
|
||||
use-node-version=20.13.1
|
||||
node-version=20.13.1
|
|
@ -1,20 +0,0 @@
|
|||
# Reference-- https://pnpm.io/docker
|
||||
|
||||
FROM node:20-alpine AS base
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
RUN corepack enable
|
||||
WORKDIR /app
|
||||
COPY ./package.json /app
|
||||
EXPOSE 3939
|
||||
|
||||
FROM base AS dev
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install
|
||||
CMD ["pnpm", "run", "dev"]
|
||||
|
||||
FROM base
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod
|
||||
COPY ./index.js /app
|
||||
ENTRYPOINT ["pnpm"]
|
||||
CMD ["start"]
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
# link2cid
|
||||
|
||||
## Motivation
|
||||
|
||||
I wish I could give [kubo](https://github.com/ipfs/kubo) or [IPFS cluster](https://ipfscluster.io/) a URI to a file and then they would download the file and add to ipfs, returning me a [CID](https://docs.ipfs.tech/concepts/glossary/#cid).
|
||||
|
||||
However, neither kubo nor IPFS cluster can do this.
|
||||
|
||||
link2cid solves this issue with a REST API for adding a file at `url` to IPFS.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Configure environment
|
||||
|
||||
Create a `.env` file. See `.env.example` for an example. Important environment variables are `API_KEY`, `PORT`, and `IPFS_URL`.
|
||||
|
||||
Install and run
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm start
|
||||
```
|
||||
|
||||
Make a GET REST request to `/add` with `url` as a query parameter. Expect a [SSE](https://wikipedia.org/wiki/Server-sent_events) response.
|
||||
|
||||
## dokku
|
||||
|
||||
dokku builder-dockerfile:set link2cid dockerfile-path link2cid.Dockerfile
|
||||
|
||||
|
||||
### Examples
|
||||
|
||||
#### [HTTPIE](https://httpie.io)
|
||||
|
||||
```bash
|
||||
http -A bearer -a $API_KEY --stream 'http://localhost:3939/add?url=https://upload.wikimedia.org/wikipedia/commons/7/70/Example.png' Accept:text/event-stream
|
||||
HTTP/1.1 200 OK
|
||||
Access-Control-Allow-Origin: *
|
||||
Cache-Control: no-cache
|
||||
Connection: keep-alive
|
||||
Content-Type: text/event-stream; charset=utf-8
|
||||
Date: Thu, 21 Dec 2023 11:20:24 GMT
|
||||
Transfer-Encoding: identity
|
||||
X-Powered-By: Express
|
||||
|
||||
:ok
|
||||
|
||||
event: dlProgress
|
||||
data: {
|
||||
"percent": 100
|
||||
}
|
||||
|
||||
event: addProgress
|
||||
data: {
|
||||
"percent": 100
|
||||
}
|
||||
|
||||
event: end
|
||||
data: {
|
||||
"cid": "bafkreidj3jo7efguloaixz6vgivljlmowagagjtqv4yanyqgty2hrvg6km"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### Javascript
|
||||
|
||||
@todo this is incomplete/untested
|
||||
|
||||
```js
|
||||
await fetch('http://localhost:3939/add?url=https://upload.wikimedia.org/wikipedia/commons/7/70/Example.png', {
|
||||
headers: {
|
||||
'accept': 'text/event-stream',
|
||||
'authorization': `Bearer ${API_KEY}`
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## Dev notes
|
||||
|
||||
### Generate API_KEY
|
||||
|
||||
```js
|
||||
require('crypto').randomBytes(64).toString('hex')
|
||||
```
|
||||
|
||||
### `TypeError: data.split is not a function`
|
||||
|
||||
If you see this error, make sure data in SSE event payload is a string, not a number.
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"healthchecks": {
|
||||
"web": [
|
||||
{
|
||||
"type": "startup",
|
||||
"name": "web check",
|
||||
"description": "Checking for expecting string at /health",
|
||||
"path": "/health",
|
||||
"content": "link2cid",
|
||||
"attempts": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
require('dotenv').config()
|
||||
const app = require('./src/app.js')
|
||||
const port = process.env.PORT || 3000
|
||||
const version = require('./package.json').version
|
||||
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`link2cid ${version} listening on port ${port}`)
|
||||
})
|
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
"name": "@futureporn/link2cid",
|
||||
"version": "4.3.0",
|
||||
"description": "REST API for adding files via URL to IPFS",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "mocha \"./src/**/*.spec.js\"",
|
||||
"dev": "pnpm nodemon ./index.js",
|
||||
"start": "node index.js"
|
||||
},
|
||||
"keywords": [
|
||||
"IPFS",
|
||||
"CID",
|
||||
"HTTP",
|
||||
"REST"
|
||||
],
|
||||
"author": "@CJ_Clippy",
|
||||
"license": "Unlicense",
|
||||
"dependencies": {
|
||||
"@paralleldrive/cuid2": "^2.2.2",
|
||||
"@types/express": "^4.17.21",
|
||||
"better-queue": "^3.8.12",
|
||||
"body-parser": "^1.20.2",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^5.1.0",
|
||||
"mocha": "^10.4.0",
|
||||
"nodemon": "^3.0.3",
|
||||
"supertest": "^6.3.4"
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,33 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
require('dotenv').config();
|
||||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
const cors = require('cors');
|
||||
const fs = require('fs');
|
||||
const fsp = require('fs/promises');
|
||||
const { openAsBlob } = require('node:fs');
|
||||
const { rm, stat } = require('fs/promises');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const { authenticate } = require('./middleware/auth.js')
|
||||
const { createTask, readTask, deleteTask } = require('./models/task.js')
|
||||
const readHeath = require('./models/health.js')
|
||||
const store = require('./middleware/store.js');
|
||||
const queue = require('./middleware/queue.js');
|
||||
|
||||
const app = express();
|
||||
app.use(store);
|
||||
app.use(queue);
|
||||
app.use(cors());
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
|
||||
|
||||
app.post('/task', authenticate, createTask)
|
||||
app.get('/task', readTask)
|
||||
app.delete('/task', authenticate, deleteTask)
|
||||
app.get('/health', readHeath)
|
||||
|
||||
|
||||
module.exports = app
|
|
@ -1,109 +0,0 @@
|
|||
|
||||
const app = require('./app.js')
|
||||
const request = require('supertest')
|
||||
const qs = require('querystring')
|
||||
require('dotenv').config()
|
||||
|
||||
describe('app', function () {
|
||||
it('should exist', function (done) {
|
||||
if (!app?.mountpath) throw new Error('app doesnt exist');
|
||||
done()
|
||||
})
|
||||
|
||||
|
||||
describe('/health', function () {
|
||||
it('should be publicly readable', function (done) {
|
||||
request(app)
|
||||
.get('/health')
|
||||
.set('Accept', 'text/html')
|
||||
.expect('Content-Type', /text/)
|
||||
.expect(/piss/)
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('/task', function () {
|
||||
describe('POST', function () {
|
||||
it('should create a task', function (done) {
|
||||
request(app)
|
||||
.post('/task')
|
||||
.set('Authorization', `Bearer ${process.env.API_KEY}`)
|
||||
.set('Accept', 'application/json')
|
||||
.send({
|
||||
url: 'https://futureporn-b2.b-cdn.net/projekt-melody.jpg'
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect((res) => {
|
||||
if (!res.body?.data) throw new Error('response body was missing data')
|
||||
if (!res.body?.data?.id) throw new Error('response body was missing id')
|
||||
return true
|
||||
})
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
describe('GET', function () {
|
||||
it('should show all tasks specifications', async function () {
|
||||
await request(app).post('/task').set('Authorization', `Bearer ${process.env.API_KEY}`).send({ url: 'https://example.com/my.jpg' })
|
||||
await request(app).post('/task').set('Authorization', `Bearer ${process.env.API_KEY}`).send({ url: 'https://example.com/your.png' })
|
||||
return request(app)
|
||||
.get(`/task`)
|
||||
.set('Authorization', `Bearer ${process.env.API_KEY}`)
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect((res) => {
|
||||
if (!res?.body?.data) throw new Error('there was no data in response')
|
||||
})
|
||||
.expect(200)
|
||||
})
|
||||
it('should accept task id as query param and return task specification', function (done) {
|
||||
const seed = request(app).post('/task').set('Authorization', `Bearer ${process.env.API_KEY}`).send({ url: 'https://example.com/z.jpg' })
|
||||
seed.then((res) => {
|
||||
|
||||
const query = qs.stringify({
|
||||
id: res.body.data.id
|
||||
})
|
||||
request(app)
|
||||
.get(`/task?${query}`)
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect((res) => {
|
||||
if (res?.body?.error) throw new Error('there was an error in the response: '+res.body?.message)
|
||||
if (!res?.body?.data?.url) throw new Error('data.url was missing')
|
||||
if (!res?.body?.data?.createdAt) throw new Error('data.createdAt was missing')
|
||||
return true
|
||||
})
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
it('should show all tasks by default', function (done) {
|
||||
request(app)
|
||||
.get('/task')
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect((res) => {
|
||||
if (res.body?.error) throw new Error('there was an error in the response'+res.error)
|
||||
if (!res.body?.data) throw new Error('data was missing')
|
||||
return true
|
||||
})
|
||||
.expect(200, done)
|
||||
|
||||
})
|
||||
})
|
||||
describe('DELETE', function () {
|
||||
const query = qs.stringify({
|
||||
id: 'awejf9wiejf9we'
|
||||
})
|
||||
it('should delete a single task', function (done) {
|
||||
request(app)
|
||||
.delete(`/task?${query}`)
|
||||
.set('Authorization', `Bearer ${process.env.API_KEY}`)
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done);
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
||||
})
|
|
@ -1 +0,0 @@
|
|||
hello worlds
|
|
@ -1,16 +0,0 @@
|
|||
|
||||
module.exports.authenticate = function authenticate(req, res, next) {
|
||||
const bearerToken = req.headers?.authorization.split(' ').at(1);
|
||||
if (!bearerToken) {
|
||||
const msg = `authorization bearer token was missing from request headers`;
|
||||
console.error(msg);
|
||||
return res.status(401).json({ error: true, message: msg });
|
||||
}
|
||||
if (bearerToken !== process.env.API_KEY) {
|
||||
const msg = 'INCORRECT API_KEY (wrong token)';
|
||||
console.error(msg);
|
||||
return res.status(403).json({ error: true, message: msg });
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
const Queue = require('better-queue');
|
||||
const taskProcess = require('../utils/taskProcess.js');
|
||||
|
||||
const options = {
|
||||
id: 'id',
|
||||
maxRetries: 3,
|
||||
concurrent: 1
|
||||
// @todo better-queue has batching and concurrency. might be useful to implement in the future
|
||||
// @see https://github.com/diamondio/better-queue?tab=readme-ov-file#queue-management
|
||||
}
|
||||
|
||||
let q = new Queue(taskProcess, options)
|
||||
|
||||
|
||||
// Middleware function to attach db to request
|
||||
const queueMiddleware = (req, res, next) => {
|
||||
req.queue = q;
|
||||
next();
|
||||
};
|
||||
|
||||
module.exports = queueMiddleware
|
|
@ -1,10 +0,0 @@
|
|||
const store = {
|
||||
tasks: {}
|
||||
}
|
||||
|
||||
const storeMiddleware = (req, res, next) => {
|
||||
req.store = store
|
||||
next();
|
||||
};
|
||||
|
||||
module.exports = storeMiddleware;
|
|
@ -1,3 +0,0 @@
|
|||
module.exports = function readHealth (req, res) {
|
||||
return res.send('**link2cid pisses on the floor**')
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
const { createId } = require('@paralleldrive/cuid2');
|
||||
const { getTmpFilePath } = require('../utils/paths.js');
|
||||
const fsp = require('fs/promises');
|
||||
|
||||
|
||||
module.exports.createTask = function createTask (req, res) {
|
||||
|
||||
const url = req.body.url
|
||||
const task = {
|
||||
id: createId(),
|
||||
url: url,
|
||||
filePath: getTmpFilePath(url),
|
||||
fileSize: null,
|
||||
createdAt: new Date().toISOString(),
|
||||
cid: null,
|
||||
downloadProgress: null,
|
||||
addProgress: null
|
||||
}
|
||||
|
||||
if (!req?.body?.url) return res.status(400).json({ error: true, message: 'request body was missing a url' });
|
||||
|
||||
req.store.tasks[task.id] = task;
|
||||
req.queue.push(task, function (err, result) {
|
||||
if (err) throw err;
|
||||
console.log('the following is the result of the queued task being complete')
|
||||
console.log(result)
|
||||
})
|
||||
|
||||
return res.json({ error: false, data: task })
|
||||
|
||||
}
|
||||
module.exports.readTask = function readTask (req, res) {
|
||||
const id = req?.query?.id
|
||||
|
||||
// If we get an id in the query, show the one task.
|
||||
// Otherwise, we show all tasks.
|
||||
if (!!id) {
|
||||
const task = req.store.tasks[id]
|
||||
if (!task) return res.json({ error: true, message: 'there was no task in the store with that id' });
|
||||
return res.json({ error: false, data: task })
|
||||
} else {
|
||||
const tasks = req.store.tasks
|
||||
return res.json({ error: false, data: tasks })
|
||||
}
|
||||
|
||||
}
|
||||
module.exports.deleteTask = async function deleteTask (req, res) {
|
||||
const id = req?.query?.id;
|
||||
const task = req.store.tasks[id];
|
||||
|
||||
try {
|
||||
if (task?.filePath) await fsp.unlink(task.filePath);
|
||||
} catch (err) {}
|
||||
delete req.store.tasks[id];
|
||||
return res.json({ error: false, message: 'task deleted' });
|
||||
if (err) return res.json({ error: true, message: err });
|
||||
}
|
||||
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
const fs = require("fs");
|
||||
const { Readable } = require('stream');
|
||||
const { finished } = require('stream/promises');
|
||||
const path = require("path");
|
||||
|
||||
|
||||
/**
|
||||
* Download a file at url to local disk filePath
|
||||
* @param {String} url
|
||||
* @param {String} filePath
|
||||
*
|
||||
* greetz https://stackoverflow.com/a/51302466/1004931
|
||||
*/
|
||||
const download = (async (url, filePath) => {
|
||||
const res = await fetch(url);
|
||||
const fileStream = fs.createWriteStream(filePath, { flags: 'wx' });
|
||||
await finished(Readable.fromWeb(res.body).pipe(fileStream));
|
||||
});
|
||||
|
||||
module.exports = download;
|
|
@ -1,12 +0,0 @@
|
|||
const download = require('./download.js');
|
||||
const fsp = require('fs/promises');
|
||||
|
||||
describe('download', function () {
|
||||
it('should download a file from url', async function () {
|
||||
const testFilePath = '/tmp/pmel.jpg'
|
||||
try {
|
||||
await fsp.unlink(testFilePath)
|
||||
} catch (e) {}
|
||||
await download('https://futureporn-b2.b-cdn.net/projekt-melody.jpg', testFilePath)
|
||||
})
|
||||
})
|
|
@ -1,100 +0,0 @@
|
|||
|
||||
require('dotenv').config();
|
||||
const { openAsBlob } = require('node:fs');
|
||||
const { rm, stat } = require('fs/promises');
|
||||
const path = require('path');
|
||||
|
||||
if (!process.env.IPFS_URL) throw new Error('IPFS_URL was missing in env');
|
||||
|
||||
|
||||
async function streamingPostFetch(
|
||||
url,
|
||||
formData,
|
||||
basename,
|
||||
filesize
|
||||
) {
|
||||
// console.log(`streamingPostFetch with url=${url}, formData=${formData.get('file')}, basename=${basename}, filesize=${filesize}`);
|
||||
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`HTTP error! Status-- ${res.status}`);
|
||||
}
|
||||
|
||||
const reader = res.body?.getReader();
|
||||
if (!reader) {
|
||||
throw new Error('Failed to get reader from response body');
|
||||
}
|
||||
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
|
||||
const chunk = new TextDecoder().decode(value);
|
||||
const lines = chunk.split('\n');
|
||||
for (const line of lines) {
|
||||
const trimmedLine = line.trim()
|
||||
if (!!trimmedLine) {
|
||||
// console.log(trimmedLine);
|
||||
const json = JSON.parse(trimmedLine);
|
||||
// console.log(`comparing json.Name=${json.Name} with basename=${basename}`);
|
||||
if (json.Name === basename && json.Hash && json.Size) {
|
||||
// this is the last chunk
|
||||
return json;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (done) {
|
||||
throw new Error('Response reader finished before receiving a CID which indicates a failiure.');
|
||||
}
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('An error occurred:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function getFormStuff(filePath) {
|
||||
const url = `${process.env.IPFS_URL}/api/v0/add?progress=false&cid-version=1&pin=true`;
|
||||
const blob = await openAsBlob(filePath);
|
||||
const basename = path.basename(filePath);
|
||||
const filesize = (await stat(filePath)).size;
|
||||
const formData = new FormData();
|
||||
return {
|
||||
url,
|
||||
blob,
|
||||
basename,
|
||||
filesize,
|
||||
formData
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {String} filePath
|
||||
* @returns {String} CID
|
||||
*/
|
||||
const ipfsAdd = async function (filePath) {
|
||||
|
||||
|
||||
const { url: kuboUrl, blob, basename, filesize, formData } = await getFormStuff(filePath);
|
||||
|
||||
|
||||
formData.append('file', blob, basename);
|
||||
const output = await streamingPostFetch(kuboUrl, formData, basename, filesize);
|
||||
if (!output?.Hash) throw new Error('No CID was received from remote IPFS node.');
|
||||
const cid = output.Hash;
|
||||
|
||||
|
||||
return cid
|
||||
}
|
||||
|
||||
module.exports = ipfsAdd;
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
const ipfsAdd = require('./ipfsAdd.js')
|
||||
const path = require('path');
|
||||
|
||||
describe('ipfs', function () {
|
||||
describe('ipfsAdd', function () {
|
||||
it('should add a file from disk to ipfs and return a {string} CID', async function () {
|
||||
const expectedCid = 'bafkreibxh3ly47pr3emvrqtax6ieq2ybom4ywyil3yurxnlwirtcvb5pfi'
|
||||
const file = path.join(__dirname, '..', 'fixtures', 'hello-worlds.txt')
|
||||
const cid = await ipfsAdd(file, { cidVersion: 1 })
|
||||
if (cid !== expectedCid) throw new Error(`expected ${cid} to match ${expectedCid}`)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,12 +0,0 @@
|
|||
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const getTmpFilePath = function (url) {
|
||||
const timestamp = new Date().valueOf()
|
||||
return path.join(os.tmpdir(), timestamp+'-'+path.basename(url))
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getTmpFilePath
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
const paths = require('./paths.js')
|
||||
|
||||
describe('paths', function () {
|
||||
describe('getTmpFilePath', function () {
|
||||
it('should accept a url and receive a /tmp/<datestamp><basename> path on disk', function () {
|
||||
const url = 'https://example.com/my.jpg'
|
||||
const p = paths.getTmpFilePath(url)
|
||||
if (!/\/tmp\/\d+-my\.jpg/.test(p)) throw new Error(`expected ${p} to use format /tmp/<datestamp><basename>`)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,25 +0,0 @@
|
|||
const os = require('os');
|
||||
const path = require('path');
|
||||
const downloadFile = require('./download');
|
||||
const ipfsAdd = require('./ipfsAdd');
|
||||
|
||||
|
||||
const taskProcess = async function (taskSpec, cb) {
|
||||
|
||||
console.log('downloading')
|
||||
this.progressTask(1, 3, "downloading")
|
||||
await downloadFile(taskSpec.url, taskSpec.filePath)
|
||||
|
||||
console.log('adding')
|
||||
this.progressTask(2, 3, "adding")
|
||||
|
||||
const cid = await ipfsAdd(taskSpec.filePath)
|
||||
|
||||
taskSpec.cid = cid
|
||||
|
||||
cb(null, taskSpec)
|
||||
|
||||
}
|
||||
|
||||
|
||||
module.exports = taskProcess;
|
|
@ -1,44 +0,0 @@
|
|||
const http = require('node:http');
|
||||
const faye = require('faye');
|
||||
const url = require('url');
|
||||
const pkg = require('./package.json')
|
||||
require('dotenv').config()
|
||||
|
||||
var server = http.createServer(),
|
||||
bayeux = new faye.NodeAdapter({ mount: '/faye' });
|
||||
|
||||
|
||||
bayeux.on('subscribe', function (clientId, channel) {
|
||||
console.log(`bayeux client ${clientId} subscribed to ${channel}`)
|
||||
})
|
||||
|
||||
const client = bayeux.getClient()
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
function publish(msg) {
|
||||
client.publish('/signals', {
|
||||
text: msg
|
||||
})
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
publish('Hello mocha!')
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
|
||||
|
||||
server.on('request', (req, res) => {
|
||||
const parsedUrl = url.parse(req.url, true);
|
||||
if (parsedUrl.pathname === '/metrics') {
|
||||
// Handle the /metrics route
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.end(`version=${pkg.version}`);
|
||||
}
|
||||
});
|
||||
|
||||
bayeux.attach(server);
|
||||
const port = process.env.PORT || 5000;
|
||||
console.log(`listening on ${port}`);
|
||||
server.listen(port);
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"name": "futureporn-realtime",
|
||||
"version": "1.1.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"dev": "nodemon index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "Unlicense",
|
||||
"dependencies": {
|
||||
"dotenv": "^16.4.5",
|
||||
"faye": "^1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.4.1",
|
||||
"mocha": "^10.4.0"
|
||||
}
|
||||
}
|
|
@ -1,754 +0,0 @@
|
|||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
dotenv:
|
||||
specifier: ^16.4.5
|
||||
version: 16.4.5
|
||||
faye:
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
devDependencies:
|
||||
chai:
|
||||
specifier: ^4.4.1
|
||||
version: 4.4.1
|
||||
mocha:
|
||||
specifier: ^10.4.0
|
||||
version: 10.4.0
|
||||
|
||||
packages:
|
||||
|
||||
ansi-colors@4.1.1:
|
||||
resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
ansi-regex@5.0.1:
|
||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
ansi-styles@4.3.0:
|
||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
anymatch@3.1.3:
|
||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
asap@2.0.6:
|
||||
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
|
||||
|
||||
assertion-error@1.1.0:
|
||||
resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
|
||||
|
||||
balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
binary-extensions@2.3.0:
|
||||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
brace-expansion@2.0.1:
|
||||
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
|
||||
|
||||
braces@3.0.2:
|
||||
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
browser-stdout@1.3.1:
|
||||
resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==}
|
||||
|
||||
camelcase@6.3.0:
|
||||
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
chai@4.4.1:
|
||||
resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
chalk@4.1.2:
|
||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
check-error@1.0.3:
|
||||
resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==}
|
||||
|
||||
chokidar@3.5.3:
|
||||
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
|
||||
engines: {node: '>= 8.10.0'}
|
||||
|
||||
cliui@7.0.4:
|
||||
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
|
||||
|
||||
color-convert@2.0.1:
|
||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||
engines: {node: '>=7.0.0'}
|
||||
|
||||
color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
|
||||
csprng@0.1.2:
|
||||
resolution: {integrity: sha512-D3WAbvvgUVIqSxUfdvLeGjuotsB32bvfVPd+AaaTWMtyUeC9zgCnw5xs94no89yFLVsafvY9dMZEhTwsY/ZecA==}
|
||||
engines: {node: '>=0.6.0'}
|
||||
|
||||
debug@4.3.4:
|
||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
decamelize@4.0.0:
|
||||
resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
deep-eql@4.1.3:
|
||||
resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
diff@5.0.0:
|
||||
resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
|
||||
dotenv@16.4.5:
|
||||
resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
|
||||
escalade@3.1.2:
|
||||
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
escape-string-regexp@4.0.0:
|
||||
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
faye-websocket@0.11.4:
|
||||
resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==}
|
||||
engines: {node: '>=0.8.0'}
|
||||
|
||||
faye@1.4.0:
|
||||
resolution: {integrity: sha512-kRrIg4be8VNYhycS2PY//hpBJSzZPr/DBbcy9VWelhZMW3KhyLkQR0HL0k0MNpmVoNFF4EdfMFkNAWjTP65g6w==}
|
||||
engines: {node: '>=0.8.0'}
|
||||
|
||||
fill-range@7.0.1:
|
||||
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
find-up@5.0.0:
|
||||
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
flat@5.0.2:
|
||||
resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==}
|
||||
hasBin: true
|
||||
|
||||
fs.realpath@1.0.0:
|
||||
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
get-caller-file@2.0.5:
|
||||
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||
engines: {node: 6.* || 8.* || >= 10.*}
|
||||
|
||||
get-func-name@2.0.2:
|
||||
resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
|
||||
|
||||
glob-parent@5.1.2:
|
||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
glob@8.1.0:
|
||||
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
has-flag@4.0.0:
|
||||
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
he@1.2.0:
|
||||
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
|
||||
hasBin: true
|
||||
|
||||
http-parser-js@0.5.8:
|
||||
resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==}
|
||||
|
||||
inflight@1.0.6:
|
||||
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
|
||||
|
||||
inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
is-extglob@2.1.1:
|
||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-fullwidth-code-point@3.0.0:
|
||||
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
is-glob@4.0.3:
|
||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-number@7.0.0:
|
||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
|
||||
is-plain-obj@2.1.0:
|
||||
resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
is-unicode-supported@0.1.0:
|
||||
resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
js-yaml@4.1.0:
|
||||
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||
hasBin: true
|
||||
|
||||
locate-path@6.0.0:
|
||||
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
log-symbols@4.1.0:
|
||||
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
loupe@2.3.7:
|
||||
resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
|
||||
|
||||
minimatch@5.0.1:
|
||||
resolution: {integrity: sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
mocha@10.4.0:
|
||||
resolution: {integrity: sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
hasBin: true
|
||||
|
||||
ms@2.1.2:
|
||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
normalize-path@3.0.0:
|
||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
|
||||
p-limit@3.1.0:
|
||||
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
p-locate@5.0.0:
|
||||
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
path-exists@4.0.0:
|
||||
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
pathval@1.1.1:
|
||||
resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
|
||||
|
||||
picomatch@2.3.1:
|
||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
psl@1.9.0:
|
||||
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
|
||||
|
||||
punycode@2.3.1:
|
||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
querystringify@2.2.0:
|
||||
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
|
||||
|
||||
randombytes@2.1.0:
|
||||
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
|
||||
|
||||
readdirp@3.6.0:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||
engines: {node: '>=8.10.0'}
|
||||
|
||||
require-directory@2.1.1:
|
||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
requires-port@1.0.0:
|
||||
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
||||
|
||||
safe-buffer@5.2.1:
|
||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||
|
||||
sequin@0.1.1:
|
||||
resolution: {integrity: sha512-hJWMZRwP75ocoBM+1/YaCsvS0j5MTPeBHJkS2/wruehl9xwtX30HlDF1Gt6UZ8HHHY8SJa2/IL+jo+JJCd59rA==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
|
||||
serialize-javascript@6.0.0:
|
||||
resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==}
|
||||
|
||||
string-width@4.2.3:
|
||||
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
strip-ansi@6.0.1:
|
||||
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
strip-json-comments@3.1.1:
|
||||
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
supports-color@7.2.0:
|
||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
supports-color@8.1.1:
|
||||
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
|
||||
tough-cookie@4.1.4:
|
||||
resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
tunnel-agent@0.6.0:
|
||||
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
|
||||
|
||||
type-detect@4.0.8:
|
||||
resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
universalify@0.2.0:
|
||||
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
|
||||
engines: {node: '>= 4.0.0'}
|
||||
|
||||
url-parse@1.5.10:
|
||||
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
|
||||
|
||||
websocket-driver@0.7.4:
|
||||
resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==}
|
||||
engines: {node: '>=0.8.0'}
|
||||
|
||||
websocket-extensions@0.1.4:
|
||||
resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==}
|
||||
engines: {node: '>=0.8.0'}
|
||||
|
||||
workerpool@6.2.1:
|
||||
resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==}
|
||||
|
||||
wrap-ansi@7.0.0:
|
||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
y18n@5.0.8:
|
||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
yargs-parser@20.2.4:
|
||||
resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
yargs-unparser@2.0.0:
|
||||
resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
yargs@16.2.0:
|
||||
resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
yocto-queue@0.1.0:
|
||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
snapshots:
|
||||
|
||||
ansi-colors@4.1.1: {}
|
||||
|
||||
ansi-regex@5.0.1: {}
|
||||
|
||||
ansi-styles@4.3.0:
|
||||
dependencies:
|
||||
color-convert: 2.0.1
|
||||
|
||||
anymatch@3.1.3:
|
||||
dependencies:
|
||||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.1
|
||||
|
||||
argparse@2.0.1: {}
|
||||
|
||||
asap@2.0.6: {}
|
||||
|
||||
assertion-error@1.1.0: {}
|
||||
|
||||
balanced-match@1.0.2: {}
|
||||
|
||||
binary-extensions@2.3.0: {}
|
||||
|
||||
brace-expansion@2.0.1:
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
|
||||
braces@3.0.2:
|
||||
dependencies:
|
||||
fill-range: 7.0.1
|
||||
|
||||
browser-stdout@1.3.1: {}
|
||||
|
||||
camelcase@6.3.0: {}
|
||||
|
||||
chai@4.4.1:
|
||||
dependencies:
|
||||
assertion-error: 1.1.0
|
||||
check-error: 1.0.3
|
||||
deep-eql: 4.1.3
|
||||
get-func-name: 2.0.2
|
||||
loupe: 2.3.7
|
||||
pathval: 1.1.1
|
||||
type-detect: 4.0.8
|
||||
|
||||
chalk@4.1.2:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
supports-color: 7.2.0
|
||||
|
||||
check-error@1.0.3:
|
||||
dependencies:
|
||||
get-func-name: 2.0.2
|
||||
|
||||
chokidar@3.5.3:
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
braces: 3.0.2
|
||||
glob-parent: 5.1.2
|
||||
is-binary-path: 2.1.0
|
||||
is-glob: 4.0.3
|
||||
normalize-path: 3.0.0
|
||||
readdirp: 3.6.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
cliui@7.0.4:
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
wrap-ansi: 7.0.0
|
||||
|
||||
color-convert@2.0.1:
|
||||
dependencies:
|
||||
color-name: 1.1.4
|
||||
|
||||
color-name@1.1.4: {}
|
||||
|
||||
csprng@0.1.2:
|
||||
dependencies:
|
||||
sequin: 0.1.1
|
||||
|
||||
debug@4.3.4(supports-color@8.1.1):
|
||||
dependencies:
|
||||
ms: 2.1.2
|
||||
supports-color: 8.1.1
|
||||
|
||||
decamelize@4.0.0: {}
|
||||
|
||||
deep-eql@4.1.3:
|
||||
dependencies:
|
||||
type-detect: 4.0.8
|
||||
|
||||
diff@5.0.0: {}
|
||||
|
||||
dotenv@16.4.5: {}
|
||||
|
||||
emoji-regex@8.0.0: {}
|
||||
|
||||
escalade@3.1.2: {}
|
||||
|
||||
escape-string-regexp@4.0.0: {}
|
||||
|
||||
faye-websocket@0.11.4:
|
||||
dependencies:
|
||||
websocket-driver: 0.7.4
|
||||
|
||||
faye@1.4.0:
|
||||
dependencies:
|
||||
asap: 2.0.6
|
||||
csprng: 0.1.2
|
||||
faye-websocket: 0.11.4
|
||||
safe-buffer: 5.2.1
|
||||
tough-cookie: 4.1.4
|
||||
tunnel-agent: 0.6.0
|
||||
|
||||
fill-range@7.0.1:
|
||||
dependencies:
|
||||
to-regex-range: 5.0.1
|
||||
|
||||
find-up@5.0.0:
|
||||
dependencies:
|
||||
locate-path: 6.0.0
|
||||
path-exists: 4.0.0
|
||||
|
||||
flat@5.0.2: {}
|
||||
|
||||
fs.realpath@1.0.0: {}
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
get-caller-file@2.0.5: {}
|
||||
|
||||
get-func-name@2.0.2: {}
|
||||
|
||||
glob-parent@5.1.2:
|
||||
dependencies:
|
||||
is-glob: 4.0.3
|
||||
|
||||
glob@8.1.0:
|
||||
dependencies:
|
||||
fs.realpath: 1.0.0
|
||||
inflight: 1.0.6
|
||||
inherits: 2.0.4
|
||||
minimatch: 5.0.1
|
||||
once: 1.4.0
|
||||
|
||||
has-flag@4.0.0: {}
|
||||
|
||||
he@1.2.0: {}
|
||||
|
||||
http-parser-js@0.5.8: {}
|
||||
|
||||
inflight@1.0.6:
|
||||
dependencies:
|
||||
once: 1.4.0
|
||||
wrappy: 1.0.2
|
||||
|
||||
inherits@2.0.4: {}
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
dependencies:
|
||||
binary-extensions: 2.3.0
|
||||
|
||||
is-extglob@2.1.1: {}
|
||||
|
||||
is-fullwidth-code-point@3.0.0: {}
|
||||
|
||||
is-glob@4.0.3:
|
||||
dependencies:
|
||||
is-extglob: 2.1.1
|
||||
|
||||
is-number@7.0.0: {}
|
||||
|
||||
is-plain-obj@2.1.0: {}
|
||||
|
||||
is-unicode-supported@0.1.0: {}
|
||||
|
||||
js-yaml@4.1.0:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
|
||||
locate-path@6.0.0:
|
||||
dependencies:
|
||||
p-locate: 5.0.0
|
||||
|
||||
log-symbols@4.1.0:
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
is-unicode-supported: 0.1.0
|
||||
|
||||
loupe@2.3.7:
|
||||
dependencies:
|
||||
get-func-name: 2.0.2
|
||||
|
||||
minimatch@5.0.1:
|
||||
dependencies:
|
||||
brace-expansion: 2.0.1
|
||||
|
||||
mocha@10.4.0:
|
||||
dependencies:
|
||||
ansi-colors: 4.1.1
|
||||
browser-stdout: 1.3.1
|
||||
chokidar: 3.5.3
|
||||
debug: 4.3.4(supports-color@8.1.1)
|
||||
diff: 5.0.0
|
||||
escape-string-regexp: 4.0.0
|
||||
find-up: 5.0.0
|
||||
glob: 8.1.0
|
||||
he: 1.2.0
|
||||
js-yaml: 4.1.0
|
||||
log-symbols: 4.1.0
|
||||
minimatch: 5.0.1
|
||||
ms: 2.1.3
|
||||
serialize-javascript: 6.0.0
|
||||
strip-json-comments: 3.1.1
|
||||
supports-color: 8.1.1
|
||||
workerpool: 6.2.1
|
||||
yargs: 16.2.0
|
||||
yargs-parser: 20.2.4
|
||||
yargs-unparser: 2.0.0
|
||||
|
||||
ms@2.1.2: {}
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
normalize-path@3.0.0: {}
|
||||
|
||||
once@1.4.0:
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
|
||||
p-limit@3.1.0:
|
||||
dependencies:
|
||||
yocto-queue: 0.1.0
|
||||
|
||||
p-locate@5.0.0:
|
||||
dependencies:
|
||||
p-limit: 3.1.0
|
||||
|
||||
path-exists@4.0.0: {}
|
||||
|
||||
pathval@1.1.1: {}
|
||||
|
||||
picomatch@2.3.1: {}
|
||||
|
||||
psl@1.9.0: {}
|
||||
|
||||
punycode@2.3.1: {}
|
||||
|
||||
querystringify@2.2.0: {}
|
||||
|
||||
randombytes@2.1.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
readdirp@3.6.0:
|
||||
dependencies:
|
||||
picomatch: 2.3.1
|
||||
|
||||
require-directory@2.1.1: {}
|
||||
|
||||
requires-port@1.0.0: {}
|
||||
|
||||
safe-buffer@5.2.1: {}
|
||||
|
||||
sequin@0.1.1: {}
|
||||
|
||||
serialize-javascript@6.0.0:
|
||||
dependencies:
|
||||
randombytes: 2.1.0
|
||||
|
||||
string-width@4.2.3:
|
||||
dependencies:
|
||||
emoji-regex: 8.0.0
|
||||
is-fullwidth-code-point: 3.0.0
|
||||
strip-ansi: 6.0.1
|
||||
|
||||
strip-ansi@6.0.1:
|
||||
dependencies:
|
||||
ansi-regex: 5.0.1
|
||||
|
||||
strip-json-comments@3.1.1: {}
|
||||
|
||||
supports-color@7.2.0:
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
|
||||
supports-color@8.1.1:
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
tough-cookie@4.1.4:
|
||||
dependencies:
|
||||
psl: 1.9.0
|
||||
punycode: 2.3.1
|
||||
universalify: 0.2.0
|
||||
url-parse: 1.5.10
|
||||
|
||||
tunnel-agent@0.6.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
type-detect@4.0.8: {}
|
||||
|
||||
universalify@0.2.0: {}
|
||||
|
||||
url-parse@1.5.10:
|
||||
dependencies:
|
||||
querystringify: 2.2.0
|
||||
requires-port: 1.0.0
|
||||
|
||||
websocket-driver@0.7.4:
|
||||
dependencies:
|
||||
http-parser-js: 0.5.8
|
||||
safe-buffer: 5.2.1
|
||||
websocket-extensions: 0.1.4
|
||||
|
||||
websocket-extensions@0.1.4: {}
|
||||
|
||||
workerpool@6.2.1: {}
|
||||
|
||||
wrap-ansi@7.0.0:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
|
||||
wrappy@1.0.2: {}
|
||||
|
||||
y18n@5.0.8: {}
|
||||
|
||||
yargs-parser@20.2.4: {}
|
||||
|
||||
yargs-unparser@2.0.0:
|
||||
dependencies:
|
||||
camelcase: 6.3.0
|
||||
decamelize: 4.0.0
|
||||
flat: 5.0.2
|
||||
is-plain-obj: 2.1.0
|
||||
|
||||
yargs@16.2.0:
|
||||
dependencies:
|
||||
cliui: 7.0.4
|
||||
escalade: 3.1.2
|
||||
get-caller-file: 2.0.5
|
||||
require-directory: 2.1.1
|
||||
string-width: 4.2.3
|
||||
y18n: 5.0.8
|
||||
yargs-parser: 20.2.4
|
||||
|
||||
yocto-queue@0.1.0: {}
|
|
@ -1,49 +0,0 @@
|
|||
const expect = require('chai').expect
|
||||
const Faye = require('faye')
|
||||
|
||||
// for reference of _state contants,
|
||||
// @see https://github.com/faye/faye/blob/60141e8d3942a88ba3de6a9b3aef9503bc1bb1e6/src/protocol/client.js#L178
|
||||
const UNCONNECTED = 1
|
||||
const CONNECTING = 2
|
||||
const CONNECTED = 3
|
||||
const DISCONNECTED = 4
|
||||
|
||||
describe('faye', function () {
|
||||
describe('sending event', function () {
|
||||
let client = new Faye.Client('http://localhost:3535/faye');
|
||||
// let client = new Faye.Client('https://realtime.futureporn.net/faye');
|
||||
client.connect()
|
||||
it('should send a signal', function (done) {
|
||||
const pub = client.publish('/signals', {
|
||||
text: 'hello worldy 123!'
|
||||
})
|
||||
pub.callback(function () {
|
||||
expect(client._state).to.equal(CONNECTED)
|
||||
done()
|
||||
})
|
||||
pub.errback(function (e) {
|
||||
throw new Error(e)
|
||||
})
|
||||
})
|
||||
it('should receive a signal', function (done) {
|
||||
const sub = client.subscribe('/signals', function(message) {
|
||||
console.log('Got a message: ' + message.text);
|
||||
expect(message.text).to.equal('Hello mocha!')
|
||||
done()
|
||||
});
|
||||
sub.callback(function () {
|
||||
expect(client._state).to.equal(CONNECTED)
|
||||
})
|
||||
sub.errback(function (e) {
|
||||
throw new Error(e)
|
||||
})
|
||||
})
|
||||
this.afterEach(function () {
|
||||
expect(client._state).to.equal(CONNECTED)
|
||||
})
|
||||
this.afterAll(function () {
|
||||
client.disconnect()
|
||||
expect(client._state).to.equal(DISCONNECTED)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,3 +0,0 @@
|
|||
node_modules
|
||||
lib
|
||||
.eslintrc.js
|
|
@ -1,48 +0,0 @@
|
|||
const { builtinModules } = require('module');
|
||||
|
||||
const ALLOWED_NODE_BUILTINS = new Set(['assert']);
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
plugins: ['@typescript-eslint', 'deprecation'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
],
|
||||
rules: {
|
||||
// recommended for safety
|
||||
'@typescript-eslint/no-floating-promises': 'error', // forgetting to await Activities and Workflow APIs is bad
|
||||
'deprecation/deprecation': 'warn',
|
||||
|
||||
// code style preference
|
||||
'object-shorthand': ['error', 'always'],
|
||||
|
||||
// relaxed rules, for convenience
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'warn',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['src/workflows.ts', 'src/workflows-*.ts', 'src/workflows/*.ts'],
|
||||
rules: {
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
...builtinModules.filter((m) => !ALLOWED_NODE_BUILTINS.has(m)).flatMap((m) => [m, `node:${m}`]),
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,2 +0,0 @@
|
|||
lib
|
||||
node_modules
|
|
@ -1 +0,0 @@
|
|||
20
|
|
@ -1 +0,0 @@
|
|||
lib
|
|
@ -1,2 +0,0 @@
|
|||
printWidth: 120
|
||||
singleQuote: true
|
|
@ -1,46 +0,0 @@
|
|||
# Cron Workflows
|
||||
|
||||
_DEPRECATED: use [Schedules](https://github.com/temporalio/samples-typescript/tree/main/schedules) instead._
|
||||
|
||||
This example demonstrates a working Cron workflow. Note the limitations and caveats listed in the [docs](https://docs.temporal.io/content/what-is-a-temporal-cron-job/).
|
||||
|
||||
Differences from the hello world demo:
|
||||
|
||||
- The Workflow is started with the `cronSchedule: '* * * * *',` option: [`src/client.ts`](./src/client.ts).
|
||||
- The Activity actually prints a log, instead of returning a string.
|
||||
- The Workflow runs forever, so if we want it to stop, we have to cancel it. In our `client.ts` script, we cancel it using the handle (when `Ctrl/Cmd-C` is hit). Usually, we'd use the Workflow ID to cancel—for example:
|
||||
|
||||
```js
|
||||
const handle = client.getHandle('1e793a6c-31e2-41c9-8139-53d114293a9e');
|
||||
await handle.cancel();
|
||||
```
|
||||
|
||||
Note that when we're changing code and restarting Workers, unless we cancel all previous Workflows, they may get picked up by our Worker (since we likely didn't change our Workflow name or task queue), and their output may conflict/mix with new Workflows we're starting. We can check what is still running in Temporal Web ([localhost:8088](http://localhost:8088) in case we need to kill all previous Workflows.
|
||||
|
||||
### Running this sample
|
||||
|
||||
1. `temporal server start-dev` to start [Temporal Server](https://github.com/temporalio/cli/#installation).
|
||||
2. `npm install` to install dependencies.
|
||||
3. `npm run start.watch` to start the Worker.
|
||||
4. In another shell, `npm run workflow` to run the Workflow.
|
||||
|
||||
Example Worker output:
|
||||
|
||||
```bash
|
||||
Hello from my-schedule, Temporal!
|
||||
Workflow time: 1636333860201
|
||||
Activity time: 1636333860241
|
||||
Hello from my-schedule, Temporal!
|
||||
Workflow time: 1636333920319
|
||||
Activity time: 1636333920340
|
||||
```
|
||||
|
||||
The difference between "Workflow time" and "Activity time" reflects the latency between scheduling an Activity and actually starting it.
|
||||
|
||||
Each new Workflow is `continuedAsNew` under the hood:
|
||||
|
||||
![image](https://user-images.githubusercontent.com/6764957/137712906-2a1d821b-d664-442c-8f17-a174b284c722.png)
|
||||
|
||||
And you can see the details in the event history:
|
||||
|
||||
![image](https://user-images.githubusercontent.com/6764957/137713250-f19a2987-4e9f-4e76-8e35-c17507731a20.png)
|
File diff suppressed because it is too large
Load Diff
|
@ -1,41 +0,0 @@
|
|||
{
|
||||
"name": "cron-workflows",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "tsc --build",
|
||||
"build.watch": "tsc --build --watch",
|
||||
"lint": "eslint .",
|
||||
"start": "ts-node src/worker.ts",
|
||||
"start.watch": "nodemon src/worker.ts",
|
||||
"workflow": "ts-node src/client.ts"
|
||||
},
|
||||
"nodemonConfig": {
|
||||
"execMap": {
|
||||
"ts": "ts-node"
|
||||
},
|
||||
"ext": "ts",
|
||||
"watch": [
|
||||
"src"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@temporalio/activity": "^1.9.0",
|
||||
"@temporalio/client": "^1.9.0",
|
||||
"@temporalio/worker": "^1.9.0",
|
||||
"@temporalio/workflow": "^1.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node18": "^1.0.0",
|
||||
"@types/node": "^16.11.43",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-deprecation": "^1.2.1",
|
||||
"nodemon": "^2.0.12",
|
||||
"prettier": "^2.8.8",
|
||||
"ts-node": "^10.2.1",
|
||||
"typescript": "^4.4.2"
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +0,0 @@
|
|||
import { log, activityInfo } from '@temporalio/activity';
|
||||
|
||||
export async function logTime(name: string, wfTime: string): Promise<void> {
|
||||
const { workflowExecution } = activityInfo();
|
||||
log.info(`Hello from ${workflowExecution.workflowId}, ${name}!`, { workflowTime: wfTime, activityTime: Date.now() });
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
import { Client } from '@temporalio/client';
|
||||
import { scheduledWorkflow } from './workflows';
|
||||
|
||||
// Save this to later terminate or cancel this schedule
|
||||
const workflowId = 'my-schedule';
|
||||
|
||||
async function run() {
|
||||
const client = new Client();
|
||||
|
||||
const handle = await client.workflow.start(scheduledWorkflow, {
|
||||
taskQueue: 'cron-workflows',
|
||||
workflowId: 'my-schedule', // Save this to later terminate or cancel this schedule
|
||||
cronSchedule: '* * * * *', // start every minute
|
||||
args: ['Temporal'],
|
||||
});
|
||||
console.log('Cron started');
|
||||
|
||||
try {
|
||||
await handle.result(); // await completion of Workflow, which doesn't happen since it's a cron Workflow
|
||||
} catch (err: any) {
|
||||
console.error(err.message + ':' + handle.workflowId);
|
||||
}
|
||||
}
|
||||
|
||||
// just for this demo - cancel the workflow on Ctrl+C
|
||||
process.on('SIGINT', async () => {
|
||||
const client = new Client();
|
||||
|
||||
const handle = client.workflow.getHandle(workflowId);
|
||||
await handle.cancel();
|
||||
console.log(`\nCanceled Workflow ${handle.workflowId}`);
|
||||
process.exit(0);
|
||||
});
|
||||
// you cannot catch SIGKILL
|
||||
|
||||
run().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
|
@ -1,17 +0,0 @@
|
|||
import { Worker } from '@temporalio/worker';
|
||||
import * as activities from './activities';
|
||||
|
||||
async function run() {
|
||||
// nothing different here compared to hello world
|
||||
const worker = await Worker.create({
|
||||
workflowsPath: require.resolve('./workflows'),
|
||||
activities,
|
||||
taskQueue: 'cron-workflows',
|
||||
});
|
||||
await worker.run();
|
||||
}
|
||||
|
||||
run().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
import { proxyActivities } from '@temporalio/workflow';
|
||||
import type * as activities from './activities';
|
||||
|
||||
const { logTime } = proxyActivities<typeof activities>({
|
||||
startToCloseTimeout: '1 minute',
|
||||
});
|
||||
|
||||
/** A workflow that simply calls an activity */
|
||||
export async function scheduledWorkflow(name: string): Promise<void> {
|
||||
await logTime(name, '' + Date.now());
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"extends": "@tsconfig/node18/tsconfig.json",
|
||||
"version": "4.4.2",
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib"
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
# fp-uppy
|
||||
# uppy
|
||||
|
||||
This is the server component which handles user uploads.
|
||||
|
||||
## B2 Bucket CORS configuration
|
||||
|
||||
|
|
Loading…
Reference in New Issue