@constructive-io/graphql-server 2.10.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +89 -0
  3. package/errors/404-message.d.ts +2 -0
  4. package/errors/404-message.js +232 -0
  5. package/errors/404.d.ts +2 -0
  6. package/errors/404.js +218 -0
  7. package/errors/50x.d.ts +2 -0
  8. package/errors/50x.js +216 -0
  9. package/esm/errors/404-message.js +230 -0
  10. package/esm/errors/404.js +216 -0
  11. package/esm/errors/50x.js +214 -0
  12. package/esm/index.js +2 -0
  13. package/esm/middleware/api.js +337 -0
  14. package/esm/middleware/auth.js +68 -0
  15. package/esm/middleware/cors.js +63 -0
  16. package/esm/middleware/flush.js +49 -0
  17. package/esm/middleware/gql.js +125 -0
  18. package/esm/middleware/graphile.js +84 -0
  19. package/esm/middleware/types.js +1 -0
  20. package/esm/plugins/PublicKeySignature.js +114 -0
  21. package/esm/run.js +8 -0
  22. package/esm/schema.js +86 -0
  23. package/esm/scripts/create-bucket.js +32 -0
  24. package/esm/server.js +95 -0
  25. package/esm/types.js +1 -0
  26. package/index.d.ts +2 -0
  27. package/index.js +18 -0
  28. package/middleware/api.d.ts +6 -0
  29. package/middleware/api.js +346 -0
  30. package/middleware/auth.d.ts +4 -0
  31. package/middleware/auth.js +75 -0
  32. package/middleware/cors.d.ts +14 -0
  33. package/middleware/cors.js +70 -0
  34. package/middleware/flush.d.ts +5 -0
  35. package/middleware/flush.js +54 -0
  36. package/middleware/gql.d.ts +6 -0
  37. package/middleware/gql.js +131 -0
  38. package/middleware/graphile.d.ts +4 -0
  39. package/middleware/graphile.js +91 -0
  40. package/middleware/types.d.ts +33 -0
  41. package/middleware/types.js +2 -0
  42. package/package.json +88 -0
  43. package/plugins/PublicKeySignature.d.ts +11 -0
  44. package/plugins/PublicKeySignature.js +121 -0
  45. package/run.d.ts +2 -0
  46. package/run.js +10 -0
  47. package/schema.d.ts +12 -0
  48. package/schema.js +123 -0
  49. package/scripts/create-bucket.d.ts +1 -0
  50. package/scripts/create-bucket.js +34 -0
  51. package/server.d.ts +17 -0
  52. package/server.js +102 -0
  53. package/types.d.ts +85 -0
  54. package/types.js +2 -0
@@ -0,0 +1,49 @@
1
+ import { Logger } from '@pgpmjs/logger';
2
+ import { svcCache } from '@pgpmjs/server-utils';
3
+ import { graphileCache } from 'graphile-cache';
4
+ import { getPgPool } from 'pg-cache';
5
+ import './types'; // for Request type
6
+ const log = new Logger('flush');
7
+ export const flush = async (req, res, next) => {
8
+ if (req.url === '/flush') {
9
+ // TODO: check bearer for a flush / special key
10
+ graphileCache.delete(req.svc_key);
11
+ svcCache.delete(req.svc_key);
12
+ res.status(200).send('OK');
13
+ return;
14
+ }
15
+ return next();
16
+ };
17
+ export const flushService = async (opts, databaseId) => {
18
+ const pgPool = getPgPool(opts.pg);
19
+ log.info('flushing db ' + databaseId);
20
+ const api = new RegExp(`^api:${databaseId}:.*`);
21
+ const schemata = new RegExp(`^schemata:${databaseId}:.*`);
22
+ const meta = new RegExp(`^metaschema:api:${databaseId}`);
23
+ if (!opts.api.isPublic) {
24
+ graphileCache.forEach((_, k) => {
25
+ if (api.test(k) || schemata.test(k) || meta.test(k)) {
26
+ graphileCache.delete(k);
27
+ svcCache.delete(k);
28
+ }
29
+ });
30
+ }
31
+ const svc = await pgPool.query(`SELECT *
32
+ FROM meta_public.domains
33
+ WHERE database_id = $1`, [databaseId]);
34
+ if (svc.rowCount === 0)
35
+ return;
36
+ for (const row of svc.rows) {
37
+ let key;
38
+ if (row.domain && !row.subdomain) {
39
+ key = row.domain;
40
+ }
41
+ else if (row.domain && row.subdomain) {
42
+ key = `${row.subdomain}.${row.domain}`;
43
+ }
44
+ if (key) {
45
+ graphileCache.delete(key);
46
+ svcCache.delete(key);
47
+ }
48
+ }
49
+ };
@@ -0,0 +1,125 @@
1
+ import gql from 'graphql-tag';
2
+ // DO NOT CHANGE TO domainBySubdomainAndDomain(domain: $domain, subdomain: $subdomain)
3
+ // condition is the way to handle since it will pass in null properly
4
+ // e.g. subdomain.domain or domain both work
5
+ export const ApiQuery = gql `
6
+ query ApiRoot($domain: String!, $subdomain: String) {
7
+ domains(condition: { domain: $domain, subdomain: $subdomain }) {
8
+ nodes {
9
+ api {
10
+ databaseId
11
+ dbname
12
+ roleName
13
+ anonRole
14
+ isPublic
15
+ schemaNamesFromExt: apiExtensions {
16
+ nodes {
17
+ schemaName
18
+ }
19
+ }
20
+ schemaNames: schemataByApiSchemaApiIdAndSchemaId {
21
+ nodes {
22
+ schemaName
23
+ }
24
+ }
25
+ rlsModule {
26
+ privateSchema {
27
+ schemaName
28
+ }
29
+ authenticateStrict
30
+ authenticate
31
+ currentRole
32
+ currentRoleId
33
+ }
34
+ database {
35
+ sites {
36
+ nodes {
37
+ domains {
38
+ nodes {
39
+ subdomain
40
+ domain
41
+ }
42
+ }
43
+ }
44
+ }
45
+ } # for now keep this for patches
46
+ apiModules {
47
+ nodes {
48
+ name
49
+ data
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
55
+ }
56
+ `;
57
+ export const ApiByNameQuery = gql `
58
+ query ApiByName($name: String!, $databaseId: UUID!) {
59
+ api: apiByDatabaseIdAndName(name: $name, databaseId: $databaseId) {
60
+ databaseId
61
+ dbname
62
+ roleName
63
+ anonRole
64
+ isPublic
65
+ schemaNamesFromExt: apiExtensions {
66
+ nodes {
67
+ schemaName
68
+ }
69
+ }
70
+ schemaNames: schemataByApiSchemaApiIdAndSchemaId {
71
+ nodes {
72
+ schemaName
73
+ }
74
+ }
75
+ rlsModule {
76
+ privateSchema {
77
+ schemaName
78
+ }
79
+ authenticate
80
+ authenticateStrict
81
+ currentRole
82
+ currentRoleId
83
+ }
84
+ database {
85
+ sites {
86
+ nodes {
87
+ domains {
88
+ nodes {
89
+ subdomain
90
+ domain
91
+ }
92
+ }
93
+ }
94
+ }
95
+ } # for now keep this for patches
96
+ apiModules {
97
+ nodes {
98
+ name
99
+ data
100
+ }
101
+ }
102
+ }
103
+ }
104
+ `;
105
+ export const ListOfAllDomainsOfDb = gql `
106
+ query ListApisByDatabaseId {
107
+ apis {
108
+ nodes {
109
+ id
110
+ databaseId
111
+ name
112
+ dbname
113
+ roleName
114
+ anonRole
115
+ isPublic
116
+ domains {
117
+ nodes {
118
+ domain
119
+ subdomain
120
+ }
121
+ }
122
+ }
123
+ }
124
+ }
125
+ `;
@@ -0,0 +1,84 @@
1
+ import { graphileCache } from 'graphile-cache';
2
+ import { getGraphileSettings as getSettings } from 'graphile-settings';
3
+ import { getPgPool } from 'pg-cache';
4
+ import { postgraphile } from 'postgraphile';
5
+ import './types'; // for Request type
6
+ import PublicKeySignature from '../plugins/PublicKeySignature';
7
+ export const graphile = (opts) => {
8
+ return async (req, res, next) => {
9
+ try {
10
+ const api = req.api;
11
+ if (!api) {
12
+ return res.status(500).send('Missing API info');
13
+ }
14
+ const key = req.svc_key;
15
+ if (!key) {
16
+ return res.status(500).send('Missing service cache key');
17
+ }
18
+ const { dbname, anonRole, roleName, schema } = api;
19
+ if (graphileCache.has(key)) {
20
+ const { handler } = graphileCache.get(key);
21
+ return handler(req, res, next);
22
+ }
23
+ const options = getSettings({
24
+ ...opts,
25
+ graphile: {
26
+ ...opts.graphile,
27
+ schema: schema,
28
+ },
29
+ });
30
+ const pubkey_challenge = api.apiModules.find((mod) => mod.name === 'pubkey_challenge');
31
+ if (pubkey_challenge && pubkey_challenge.data) {
32
+ options.appendPlugins.push(PublicKeySignature(pubkey_challenge.data));
33
+ }
34
+ options.appendPlugins = options.appendPlugins ?? [];
35
+ options.appendPlugins.push(...opts.graphile.appendPlugins);
36
+ options.pgSettings = async function pgSettings(request) {
37
+ const gqlReq = request;
38
+ const context = {
39
+ [`jwt.claims.database_id`]: gqlReq.databaseId,
40
+ [`jwt.claims.ip_address`]: gqlReq.clientIp,
41
+ };
42
+ if (gqlReq.get('origin')) {
43
+ context['jwt.claims.origin'] = gqlReq.get('origin');
44
+ }
45
+ if (gqlReq.get('User-Agent')) {
46
+ context['jwt.claims.user_agent'] = gqlReq.get('User-Agent');
47
+ }
48
+ if (gqlReq?.token?.user_id) {
49
+ return {
50
+ role: roleName,
51
+ [`jwt.claims.token_id`]: gqlReq.token.id,
52
+ [`jwt.claims.user_id`]: gqlReq.token.user_id,
53
+ ...context,
54
+ };
55
+ }
56
+ return { role: anonRole, ...context };
57
+ };
58
+ options.graphqlRoute = '/graphql';
59
+ options.graphiqlRoute = '/graphiql';
60
+ options.graphileBuildOptions = {
61
+ ...options.graphileBuildOptions,
62
+ ...opts.graphile.graphileBuildOptions,
63
+ };
64
+ const graphileOpts = {
65
+ ...options,
66
+ ...opts.graphile.overrideSettings,
67
+ };
68
+ const pgPool = getPgPool({
69
+ ...opts.pg,
70
+ database: dbname,
71
+ });
72
+ const handler = postgraphile(pgPool, schema, graphileOpts);
73
+ graphileCache.set(key, {
74
+ pgPool,
75
+ pgPoolKey: dbname,
76
+ handler,
77
+ });
78
+ return handler(req, res, next);
79
+ }
80
+ catch (e) {
81
+ return res.status(500).send(e.message);
82
+ }
83
+ };
84
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,114 @@
1
+ import { gql, makeExtendSchemaPlugin } from 'graphile-utils';
2
+ import pgQueryWithContext from 'pg-query-context';
3
+ export const PublicKeySignature = (pubkey_challenge) => {
4
+ const { schema, crypto_network, sign_up_with_key, sign_in_request_challenge, sign_in_record_failure, sign_in_with_challenge } = pubkey_challenge;
5
+ return makeExtendSchemaPlugin(() => ({
6
+ typeDefs: gql `
7
+ input CreateUserAccountWithPublicKeyInput {
8
+ publicKey: String!
9
+ }
10
+
11
+ input GetMessageForSigningInput {
12
+ publicKey: String!
13
+ }
14
+
15
+ input VerifyMessageForSigningInput {
16
+ publicKey: String!
17
+ message: String!
18
+ signature: String!
19
+ }
20
+
21
+ type createUserAccountWithPublicKeyPayload {
22
+ message: String!
23
+ }
24
+
25
+ type getMessageForSigningPayload {
26
+ message: String!
27
+ }
28
+
29
+ type verifyMessageForSigningPayload {
30
+ access_token: String!
31
+ access_token_expires_at: Datetime!
32
+ }
33
+
34
+ extend type Mutation {
35
+ createUserAccountWithPublicKey(
36
+ input: CreateUserAccountWithPublicKeyInput
37
+ ): createUserAccountWithPublicKeyPayload
38
+
39
+ getMessageForSigning(
40
+ input: GetMessageForSigningInput
41
+ ): getMessageForSigningPayload
42
+
43
+ verifyMessageForSigning(
44
+ input: VerifyMessageForSigningInput
45
+ ): verifyMessageForSigningPayload
46
+ }
47
+ `,
48
+ resolvers: {
49
+ Mutation: {
50
+ async createUserAccountWithPublicKey(_parent, args, context) {
51
+ const { pgClient } = context;
52
+ const { publicKey } = args.input;
53
+ await pgQueryWithContext({
54
+ client: pgClient,
55
+ context: { role: 'anonymous' },
56
+ query: `SELECT * FROM "${schema}".${sign_up_with_key}($1)`,
57
+ variables: [publicKey]
58
+ });
59
+ const { rows: [{ [sign_in_request_challenge]: message }] } = await pgQueryWithContext({
60
+ client: pgClient,
61
+ context: { role: 'anonymous' },
62
+ query: `SELECT * FROM "${schema}".${sign_in_request_challenge}($1)`,
63
+ variables: [publicKey]
64
+ });
65
+ return { message };
66
+ },
67
+ async getMessageForSigning(_parent, args, context) {
68
+ const { pgClient } = context;
69
+ const { publicKey } = args.input;
70
+ const { rows: [{ [sign_in_request_challenge]: message }] } = await pgQueryWithContext({
71
+ client: pgClient,
72
+ context: { role: 'anonymous' },
73
+ query: `SELECT * FROM "${schema}".${sign_in_request_challenge}($1)`,
74
+ variables: [publicKey]
75
+ });
76
+ if (!message)
77
+ throw new Error('NO_ACCOUNT_EXISTS');
78
+ return { message };
79
+ },
80
+ async verifyMessageForSigning(_parent, args, context) {
81
+ const { pgClient } = context;
82
+ const { publicKey, message, signature } = args.input;
83
+ // const network = Networks[crypto_network];
84
+ const network = 'btc';
85
+ // const result = verifyMessage(message, publicKey, signature, network);
86
+ // TODO implement using interchainJS?
87
+ const result = false;
88
+ if (!result) {
89
+ await pgQueryWithContext({
90
+ client: pgClient,
91
+ context: { role: 'anonymous' },
92
+ query: `SELECT * FROM "${schema}".${sign_in_record_failure}($1)`,
93
+ variables: [publicKey]
94
+ });
95
+ throw new Error('BAD_SIGNIN');
96
+ }
97
+ const { rows: [token] } = await pgQueryWithContext({
98
+ client: pgClient,
99
+ context: { role: 'anonymous' },
100
+ query: `SELECT * FROM "${schema}".${sign_in_with_challenge}($1, $2)`,
101
+ variables: [publicKey, message]
102
+ });
103
+ if (!token?.access_token)
104
+ throw new Error('BAD_SIGNIN');
105
+ return {
106
+ access_token: token.access_token,
107
+ access_token_expires_at: token.access_token_expires_at
108
+ };
109
+ }
110
+ }
111
+ }
112
+ }));
113
+ };
114
+ export default PublicKeySignature;
package/esm/run.js ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { getEnvOptions } from '@constructive-io/graphql-env';
3
+ import { GraphQLServer as server } from './server';
4
+ server(getEnvOptions({
5
+ pg: {
6
+ database: process.env.PGDATABASE,
7
+ },
8
+ }));
package/esm/schema.js ADDED
@@ -0,0 +1,86 @@
1
+ import { printSchema, getIntrospectionQuery, buildClientSchema } from 'graphql';
2
+ import { getGraphileSettings } from 'graphile-settings';
3
+ import { getPgPool } from 'pg-cache';
4
+ import { createPostGraphileSchema } from 'postgraphile';
5
+ import * as http from 'node:http';
6
+ import * as https from 'node:https';
7
+ // Build GraphQL Schema SDL directly from Postgres using PostGraphile, without HTTP.
8
+ export async function buildSchemaSDL(opts) {
9
+ const database = opts.database ?? 'constructive';
10
+ const schemas = Array.isArray(opts.schemas) ? opts.schemas : [];
11
+ const settings = getGraphileSettings({
12
+ graphile: {
13
+ schema: schemas,
14
+ ...(opts.graphile ?? {})
15
+ }
16
+ });
17
+ const pgPool = getPgPool({ database });
18
+ const schema = await createPostGraphileSchema(pgPool, schemas, settings);
19
+ return printSchema(schema);
20
+ }
21
+ // Fetch GraphQL Schema SDL from a running GraphQL endpoint via introspection.
22
+ // This centralizes GraphQL client usage in the server package to avoid duplicating deps in the CLI.
23
+ export async function fetchEndpointSchemaSDL(endpoint, opts) {
24
+ const url = new URL(endpoint);
25
+ const requestUrl = url;
26
+ const introspectionQuery = getIntrospectionQuery({ descriptions: true });
27
+ const postData = JSON.stringify({
28
+ query: introspectionQuery,
29
+ variables: null,
30
+ operationName: 'IntrospectionQuery',
31
+ });
32
+ const headers = {
33
+ 'Content-Type': 'application/json',
34
+ 'Content-Length': String(Buffer.byteLength(postData)),
35
+ };
36
+ if (opts?.headerHost) {
37
+ headers['Host'] = opts.headerHost;
38
+ }
39
+ if (opts?.auth) {
40
+ headers['Authorization'] = opts.auth;
41
+ }
42
+ if (opts?.headers) {
43
+ for (const [key, value] of Object.entries(opts.headers)) {
44
+ headers[key] = value;
45
+ }
46
+ }
47
+ const isHttps = requestUrl.protocol === 'https:';
48
+ const lib = isHttps ? https : http;
49
+ const responseData = await new Promise((resolve, reject) => {
50
+ const req = lib.request({
51
+ hostname: requestUrl.hostname,
52
+ port: (requestUrl.port ? Number(requestUrl.port) : (isHttps ? 443 : 80)),
53
+ path: requestUrl.pathname,
54
+ method: 'POST',
55
+ headers,
56
+ }, (res) => {
57
+ let data = '';
58
+ res.on('data', (chunk) => { data += chunk; });
59
+ res.on('end', () => {
60
+ if (res.statusCode && res.statusCode >= 400) {
61
+ reject(new Error(`HTTP ${res.statusCode} – ${data}`));
62
+ return;
63
+ }
64
+ resolve(data);
65
+ });
66
+ });
67
+ req.on('error', (err) => reject(err));
68
+ req.write(postData);
69
+ req.end();
70
+ });
71
+ let json;
72
+ try {
73
+ json = JSON.parse(responseData);
74
+ }
75
+ catch (e) {
76
+ throw new Error(`Failed to parse response: ${responseData}`);
77
+ }
78
+ if (json.errors) {
79
+ throw new Error('Introspection returned errors');
80
+ }
81
+ if (!json.data) {
82
+ throw new Error('No data in introspection response');
83
+ }
84
+ const schema = buildClientSchema(json.data);
85
+ return printSchema(schema);
86
+ }
@@ -0,0 +1,32 @@
1
+ // Minimal script to create a bucket in MinIO using @constructive-io/s3-utils
2
+ // Avoid strict type coupling between different @aws-sdk/client-s3 versions
3
+ // Loads graphql/server/.env by default when running via ts-node from this workspace
4
+ import 'dotenv/config';
5
+ import { S3Client } from '@aws-sdk/client-s3';
6
+ import { createS3Bucket } from '@constructive-io/s3-utils';
7
+ const BUCKET = process.env.BUCKET_NAME || 'test-bucket';
8
+ const REGION = process.env.AWS_REGION || 'us-east-1';
9
+ const ACCESS_KEY = process.env.AWS_ACCESS_KEY || process.env.AWS_ACCESS_KEY_ID || 'minioadmin';
10
+ const SECRET_KEY = process.env.AWS_SECRET_KEY ||
11
+ process.env.AWS_SECRET_ACCESS_KEY ||
12
+ 'minioadmin';
13
+ const ENDPOINT = process.env.MINIO_ENDPOINT || 'http://localhost:9000';
14
+ (async () => {
15
+ try {
16
+ const client = new S3Client({
17
+ region: REGION,
18
+ credentials: { accessKeyId: ACCESS_KEY, secretAccessKey: SECRET_KEY },
19
+ endpoint: ENDPOINT,
20
+ forcePathStyle: true,
21
+ });
22
+ // Hint downstream to apply MinIO policies
23
+ process.env.IS_MINIO = 'true';
24
+ const res = await createS3Bucket(client, BUCKET);
25
+ console.log(`[create-bucket] ${BUCKET}:`, res);
26
+ client.destroy();
27
+ }
28
+ catch (e) {
29
+ console.error('[create-bucket] error', e);
30
+ process.exitCode = 1;
31
+ }
32
+ })();
package/esm/server.js ADDED
@@ -0,0 +1,95 @@
1
+ import 'dotenv/config';
2
+ import { getEnvOptions } from '@constructive-io/graphql-env';
3
+ import { Logger } from '@pgpmjs/logger';
4
+ import { healthz, poweredBy, trustProxy } from '@pgpmjs/server-utils';
5
+ import { middleware as parseDomains } from '@constructive-io/url-domains';
6
+ import express from 'express';
7
+ // @ts-ignore
8
+ import graphqlUpload from 'graphql-upload';
9
+ import { getPgPool } from 'pg-cache';
10
+ import requestIp from 'request-ip';
11
+ import { createApiMiddleware } from './middleware/api';
12
+ import { createAuthenticateMiddleware } from './middleware/auth';
13
+ import { cors } from './middleware/cors';
14
+ import { flush, flushService } from './middleware/flush';
15
+ import { graphile } from './middleware/graphile';
16
+ const log = new Logger('server');
17
+ export const GraphQLServer = (rawOpts = {}) => {
18
+ const envOptions = getEnvOptions(rawOpts);
19
+ const app = new Server(envOptions);
20
+ app.addEventListener();
21
+ app.listen();
22
+ };
23
+ class Server {
24
+ app;
25
+ opts;
26
+ constructor(opts) {
27
+ this.opts = getEnvOptions(opts);
28
+ const app = express();
29
+ const api = createApiMiddleware(opts);
30
+ const authenticate = createAuthenticateMiddleware(opts);
31
+ healthz(app);
32
+ trustProxy(app, opts.server.trustProxy);
33
+ // Warn if a global CORS override is set in production
34
+ const fallbackOrigin = opts.server?.origin?.trim();
35
+ if (fallbackOrigin && process.env.NODE_ENV === 'production') {
36
+ if (fallbackOrigin === '*') {
37
+ log.warn('CORS wildcard ("*") is enabled in production; this effectively disables CORS and is not recommended. Prefer per-API CORS via meta schema.');
38
+ }
39
+ else {
40
+ log.warn(`CORS override origin set to ${fallbackOrigin} in production. Prefer per-API CORS via meta schema.`);
41
+ }
42
+ }
43
+ app.use(poweredBy('constructive'));
44
+ app.use(cors(fallbackOrigin));
45
+ app.use(graphqlUpload.graphqlUploadExpress());
46
+ app.use(parseDomains());
47
+ app.use(requestIp.mw());
48
+ app.use(api);
49
+ app.use(authenticate);
50
+ app.use(graphile(opts));
51
+ app.use(flush);
52
+ this.app = app;
53
+ }
54
+ listen() {
55
+ const { server } = this.opts;
56
+ this.app.listen(server?.port, server?.host, () => log.info(`listening at http://${server?.host}:${server?.port}`));
57
+ }
58
+ async flush(databaseId) {
59
+ await flushService(this.opts, databaseId);
60
+ }
61
+ getPool() {
62
+ return getPgPool(this.opts.pg);
63
+ }
64
+ addEventListener() {
65
+ const pgPool = this.getPool();
66
+ pgPool.connect(this.listenForChanges.bind(this));
67
+ }
68
+ listenForChanges(err, client, release) {
69
+ if (err) {
70
+ this.error('Error connecting with notify listener', err);
71
+ setTimeout(() => this.addEventListener(), 5000);
72
+ return;
73
+ }
74
+ client.on('notification', ({ channel, payload }) => {
75
+ if (channel === 'schema:update' && payload) {
76
+ log.info('schema:update', payload);
77
+ this.flush(payload);
78
+ }
79
+ });
80
+ client.query('LISTEN "schema:update"');
81
+ client.on('error', (e) => {
82
+ this.error('Error with database notify listener', e);
83
+ release();
84
+ this.addEventListener();
85
+ });
86
+ this.log('connected and listening for changes...');
87
+ }
88
+ log(text) {
89
+ log.info(text);
90
+ }
91
+ error(text, err) {
92
+ log.error(text, err);
93
+ }
94
+ }
95
+ export { Server };
package/esm/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './server';
2
+ export * from './schema';
package/index.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./server"), exports);
18
+ __exportStar(require("./schema"), exports);
@@ -0,0 +1,6 @@
1
+ import { PgpmOptions } from '@pgpmjs/types';
2
+ import { NextFunction, Request, Response } from 'express';
3
+ import './types';
4
+ export declare const getSubdomain: (reqDomains: string[]) => string | null;
5
+ export declare const createApiMiddleware: (opts: any) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
6
+ export declare const getApiConfig: (opts: PgpmOptions, req: Request) => Promise<any>;