Convert worker to ESM (#500)
* Convert worker to ESM
To use ESM for the worker, I created a package.json file in worker/ with `{ type: "module" }` as its sole content.
I then rewrote every import to use ESM syntax.
I also tried to set `{ type: "module" }` in the root package.json file to also use ESM in next.config.js.
However, this resulted in a weird problem: default imports were now getting imported as objects in this shape: `{ default: <defaultImport> }`.
Afaik, this should only be the case if you use "import * as foo from 'bar'" syntax: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#default_import
This is fixed by not using `{ type: "module" }` for some reason. However, then, next.config.js also doesn't support ESM import syntax anymore.
The documentation says that if you want to use ESM, you can use next.config.mjs: https://nextjs.org/docs/pages/api-reference/next-config-js
But I didn't want to use MJS extension since I don't have any experience with it. For example, not sure if it's good style to mix JS with MJS etc. So I kept the CJS import syntax there.
* Ignore worker/ during linting
I wasn't able to fix the following errors:
/home/runner/work/stacker.news/stacker.news/worker/auction.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/auction.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/earn.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/earn.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/index.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/index.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/nostr.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/nostr.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/ots.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/ots.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/repin.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/repin.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/search.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/search.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/streak.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/streak.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/trust.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/trust.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/views.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/views.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
/home/runner/work/stacker.news/stacker.news/worker/wallet.js:0:0: Parsing error: No Babel config file detected for /home/runner/work/stacker.news/stacker.news/worker/wallet.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. (null)
I tried to tell babel where to find the babel config file (.babelrc), specifying the babel config in worker/package.json under "babel", using babel.config.json etc. to no avail.
However, afaict, we don't need babel for the worker since it won't run in a browser. Babel is only used to transpile code to target browsers.
But it still would be nice to lint the worker code with standard.
But we can figure this out later.
* Fix worker imports from lib/ and api/
This fixes the issue that we can't use `{ "type": "module" }` in the root package.json since it breaks the app with this error:
app | TypeError: next_auth_providers_credentials__WEBPACK_IMPORTED_MODULE_2__ is not a function
app | at eval (webpack-internal:///./pages/api/auth/[...nextauth].js:218:20)
app | at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
app | LND GRPC connection successful
app | - error pages/api/auth/[...nextauth].js (139:2) @ CredentialsProvider
app | - error Error [TypeError]: next_auth_providers_credentials__WEBPACK_IMPORTED_MODULE_2__ is not a function
app | at eval (webpack-internal:///./pages/api/auth/[...nextauth].js:218:20) {
app | digest: undefined
app | }
app | 137 |
app | 138 | const providers = [
app | > 139 | CredentialsProvider({
app | | ^
app | 140 | id: 'lightning',
app | 141 | name: 'Lightning',
app | 142 | credentials: {
app | TypeError: next_auth_providers_credentials__WEBPACK_IMPORTED_MODULE_2__ is not a function
app | at eval (webpack-internal:///./pages/api/auth/[...nextauth].js:218:20)
app | at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
build but we need to tell the worker that the files are MJS, else we get this error:
worker | file:///app/worker/wallet.js:3
worker | import { datePivot } from '../lib/time.js'
worker | ^^^^^^^^^
worker | SyntaxError: Named export 'datePivot' not found. The requested module '../lib/time.js' is a CommonJS module, which may not support all module.exports as named exports.
worker | CommonJS modules can always be imported via the default export, for example using:
worker |
worker | import pkg from '../lib/time.js';
worker | const { datePivot } = pkg;
worker |
worker | at ModuleJob._instantiate (node:internal/modules/esm/module_job:124:21)
worker | at async ModuleJob.run (node:internal/modules/esm/module_job:190:5)
worker |
worker | Node.js v18.17.0
worker |
worker exited with code 1
* Fix global not defined in browser context
* Also ignore api/ and lib/ during linting
I did not want to do this but I was not able to fix this error in any other way I tried:
/home/ekzyis/programming/stacker.news/api/lnd/index.js:0:0: Parsing error: No Babel config file detected for /home/ekzyis/programming/stacker.news/api/lnd/index.js. Either disable config file checking with requ
ireConfigFile: false, or configure Babel so that it can find the config files. (null)
Did not want to look deeper into all this standard, eslint, babel configuration stuff ...
---------
Co-authored-by: ekzyis <ek@stacker.news>
Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
2023-09-24 01:19:35 +00:00
|
|
|
import { GraphQLError } from 'graphql'
|
|
|
|
import retry from 'async-retry'
|
|
|
|
import Prisma from '@prisma/client'
|
2024-03-20 00:37:31 +00:00
|
|
|
import { msatsToSats, numWithUnits } from '@/lib/format'
|
|
|
|
import { BALANCE_LIMIT_MSATS } from '@/lib/constants'
|
2021-05-20 01:09:32 +00:00
|
|
|
|
2024-07-01 17:02:29 +00:00
|
|
|
export default async function serialize (trx, { models, lnd }) {
|
2024-04-10 00:49:20 +00:00
|
|
|
// wrap first argument in array if not array already
|
|
|
|
const isArray = Array.isArray(trx)
|
|
|
|
if (!isArray) trx = [trx]
|
|
|
|
|
|
|
|
// conditional queries can be added inline using && syntax
|
|
|
|
// we filter any falsy value out here
|
|
|
|
trx = trx.filter(q => !!q)
|
|
|
|
|
2024-07-01 17:02:29 +00:00
|
|
|
const results = await retry(async bail => {
|
2021-05-20 17:21:11 +00:00
|
|
|
try {
|
2024-04-10 00:49:20 +00:00
|
|
|
const [, ...results] = await models.$transaction(
|
|
|
|
[models.$executeRaw`SELECT ASSERT_SERIALIZED()`, ...trx],
|
2023-07-27 00:18:42 +00:00
|
|
|
{ isolationLevel: Prisma.TransactionIsolationLevel.Serializable })
|
2024-04-10 00:49:20 +00:00
|
|
|
return results
|
2021-05-20 17:21:11 +00:00
|
|
|
} catch (error) {
|
|
|
|
console.log(error)
|
2023-11-21 23:32:22 +00:00
|
|
|
// two cases where we get insufficient funds:
|
|
|
|
// 1. plpgsql function raises
|
|
|
|
// 2. constraint violation via a prisma call
|
|
|
|
// XXX prisma does not provide a way to distinguish these cases so we
|
|
|
|
// have to check the error message
|
|
|
|
if (error.message.includes('SN_INSUFFICIENT_FUNDS') ||
|
|
|
|
error.message.includes('\\"users\\" violates check constraint \\"msats_positive\\"')) {
|
2023-07-23 15:08:43 +00:00
|
|
|
bail(new GraphQLError('insufficient funds', { extensions: { code: 'BAD_INPUT' } }))
|
2021-05-20 17:21:11 +00:00
|
|
|
}
|
|
|
|
if (error.message.includes('SN_NOT_SERIALIZABLE')) {
|
|
|
|
bail(new Error('wallet balance transaction is not serializable'))
|
|
|
|
}
|
2021-07-10 14:16:40 +00:00
|
|
|
if (error.message.includes('SN_CONFIRMED_WITHDRAWL_EXISTS')) {
|
2021-08-19 21:42:21 +00:00
|
|
|
bail(new Error('withdrawal invoice already confirmed (to withdraw again create a new invoice)'))
|
2021-07-10 14:16:40 +00:00
|
|
|
}
|
|
|
|
if (error.message.includes('SN_PENDING_WITHDRAWL_EXISTS')) {
|
2021-08-19 21:42:21 +00:00
|
|
|
bail(new Error('withdrawal invoice exists and is pending'))
|
2021-07-10 14:16:40 +00:00
|
|
|
}
|
2021-10-15 23:07:51 +00:00
|
|
|
if (error.message.includes('SN_INELIGIBLE')) {
|
|
|
|
bail(new Error('user ineligible for gift'))
|
|
|
|
}
|
2022-11-23 18:12:09 +00:00
|
|
|
if (error.message.includes('SN_UNSUPPORTED')) {
|
|
|
|
bail(new Error('unsupported action'))
|
|
|
|
}
|
|
|
|
if (error.message.includes('SN_DUPLICATE')) {
|
|
|
|
bail(new Error('duplicate not allowed'))
|
|
|
|
}
|
2021-10-15 23:07:51 +00:00
|
|
|
if (error.message.includes('SN_REVOKED_OR_EXHAUSTED')) {
|
|
|
|
bail(new Error('faucet has been revoked or is exhausted'))
|
|
|
|
}
|
2022-08-30 20:33:39 +00:00
|
|
|
if (error.message.includes('SN_INV_PENDING_LIMIT')) {
|
|
|
|
bail(new Error('too many pending invoices'))
|
|
|
|
}
|
|
|
|
if (error.message.includes('SN_INV_EXCEED_BALANCE')) {
|
2024-04-27 23:21:25 +00:00
|
|
|
bail(new Error(`pending invoices and withdrawals must not cause balance to exceed ${numWithUnits(msatsToSats(BALANCE_LIMIT_MSATS))}`))
|
2022-08-30 20:33:39 +00:00
|
|
|
}
|
2023-07-31 13:31:40 +00:00
|
|
|
if (error.message.includes('40001') || error.code === 'P2034') {
|
|
|
|
throw new Error('wallet balance serialization failure - try again')
|
|
|
|
}
|
|
|
|
if (error.message.includes('23514') || ['P2002', 'P2003', 'P2004'].includes(error.code)) {
|
|
|
|
bail(new Error('constraint failure'))
|
2022-08-30 20:33:39 +00:00
|
|
|
}
|
2021-05-20 17:21:11 +00:00
|
|
|
bail(error)
|
2021-05-20 01:09:32 +00:00
|
|
|
}
|
2021-05-20 17:21:11 +00:00
|
|
|
}, {
|
2024-01-25 23:41:50 +00:00
|
|
|
minTimeout: 10,
|
|
|
|
maxTimeout: 100,
|
|
|
|
retries: 10
|
2021-05-20 17:21:11 +00:00
|
|
|
})
|
2024-04-08 20:50:39 +00:00
|
|
|
|
2024-04-10 00:49:20 +00:00
|
|
|
// if first argument was not an array, unwrap the result
|
|
|
|
return isArray ? results : results[0]
|
2023-09-26 20:15:09 +00:00
|
|
|
}
|