@foxtware/mineral 0.1.7 → 0.1.9

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 (59) hide show
  1. package/.creds.yml.sample +22 -1
  2. package/api/loop/loop.constants.js +7 -0
  3. package/api/loop/loop.utils.js +43 -0
  4. package/api/loop/loopAllowlistItemsGet.js +42 -0
  5. package/api/loop/loopBlocklistItemsGet.js +46 -0
  6. package/api/loop/loopDestinationsGet.js +42 -0
  7. package/api/loop/loopGet.js +143 -0
  8. package/api/loop/loopReturnGet.js +73 -0
  9. package/api/loop/loopReturnsGet.js +46 -0
  10. package/api/peoplevox/peoplevox.sessions.js +87 -0
  11. package/api/peoplevox/peoplevox.utils.js +7 -15
  12. package/api/shopify/shopifyCollectionGet.js +1 -1
  13. package/api/shopify/shopifyTagsAdd.js +1 -1
  14. package/api/shopify/shopifyTagsRemove.js +1 -1
  15. package/api/stripe/_example.js +48 -0
  16. package/api/stripe/stripe.constants.js +5 -0
  17. package/api/stripe/stripe.utils.js +47 -0
  18. package/api/stripe/stripeCardCharge.js +98 -0
  19. package/api/stripe/stripeCardTokenCreate.js +70 -0
  20. package/api/stripe/stripeChargeCapture.js +63 -0
  21. package/api/stripe/stripeChargeCreate.js +70 -0
  22. package/api/stripe/stripeChargeGet.js +51 -0
  23. package/api/stripe/stripeChargesGet.js +52 -0
  24. package/api/stripe/stripeRefundCreate.js +64 -0
  25. package/api/stripe/stripeRefundGet.js +51 -0
  26. package/api/stripe/stripeRefundsGet.js +52 -0
  27. package/api/stripe/stripeTokenGet.js +51 -0
  28. package/api/supabase/supabase.utils.js +52 -0
  29. package/api/supabase/supabaseRowDelete.js +95 -0
  30. package/api/supabase/supabaseRowGet.js +58 -0
  31. package/api/supabase/supabaseRowInsert.js +56 -0
  32. package/api/supabase/supabaseRowUpdate.js +58 -0
  33. package/api/supabase/supabaseRpc.js +61 -0
  34. package/api/supabase/supabaseTableGet.js +44 -0
  35. package/api/supabase/supabaseTableGetAll.js +86 -0
  36. package/api/upstash/upstash.utils.js +36 -0
  37. package/api/upstash/upstashDel.js +46 -0
  38. package/api/upstash/upstashExists.js +46 -0
  39. package/api/upstash/upstashGet.js +46 -0
  40. package/api/upstash/upstashSet.js +64 -0
  41. package/api/utils.js +52 -1
  42. package/api/workable/workable.constants.js +3 -0
  43. package/api/workable/workable.utils.js +46 -0
  44. package/api/workable/workableGet.js +146 -0
  45. package/api/workable/workableJobCandidateCreate.js +87 -0
  46. package/api/workable/workableJobGet.js +52 -0
  47. package/api/workable/workableJobMembersGet.js +52 -0
  48. package/api/workable/workableJobStagesGet.js +52 -0
  49. package/api/workable/workableJobsGet.js +78 -0
  50. package/bin/mineral.js +6 -0
  51. package/hosting/.hosting.yml.sample +9 -11
  52. package/hosting/copyCredsToEnv.js +58 -2
  53. package/hosting/deployFromHostingYml.js +9 -2
  54. package/hosting/generateHosted.js +22 -2
  55. package/hosting/hosting.utils.js +3 -2
  56. package/hosting/hostingPreview.js +160 -0
  57. package/hosting/wrappers.js +1 -1
  58. package/package.json +9 -2
  59. package/server.utils.js +1 -0
@@ -1,7 +1,59 @@
1
1
  const fs = require('fs');
2
2
  const yaml = require('yaml');
3
3
 
4
- const copyCredsToEnv = (workspace) => {
4
+ const getValueAtCredsPath = (creds, pathParts) => {
5
+ let current = creds;
6
+
7
+ for (const pathPart of pathParts) {
8
+ if (current == null || typeof current !== 'object' || !(pathPart in current)) {
9
+ return undefined;
10
+ }
11
+
12
+ current = current[pathPart];
13
+ }
14
+
15
+ return current;
16
+ };
17
+
18
+ const setValueAtCredsPath = (target, pathParts, value) => {
19
+ let current = target;
20
+
21
+ for (let index = 0; index < pathParts.length - 1; index++) {
22
+ const pathPart = pathParts[index];
23
+
24
+ if (!(pathPart in current) || typeof current[pathPart] !== 'object' || current[pathPart] === null) {
25
+ current[pathPart] = {};
26
+ }
27
+
28
+ current = current[pathPart];
29
+ }
30
+
31
+ current[pathParts[pathParts.length - 1]] = value;
32
+ };
33
+
34
+ const filterCredsByPaths = (creds, includeCredsPaths) => {
35
+ const filteredCreds = {};
36
+
37
+ for (const credsPath of includeCredsPaths) {
38
+ if (typeof credsPath !== 'string' || !credsPath.trim()) {
39
+ throw new Error(`Invalid include_creds_paths entry: ${ credsPath }`);
40
+ }
41
+
42
+ const pathParts = credsPath.split('.');
43
+ const value = getValueAtCredsPath(creds, pathParts);
44
+
45
+ if (value === undefined) {
46
+ throw new Error(`Missing creds path "${ credsPath }" in .creds.yml`);
47
+ }
48
+
49
+ setValueAtCredsPath(filteredCreds, pathParts, value);
50
+ }
51
+
52
+ return filteredCreds;
53
+ };
54
+
55
+ const copyCredsToEnv = (workspace, options = {}) => {
56
+ const { includeCredsPaths } = options;
5
57
  const normalizedWorkspace = workspace.replace(/\/$/, '');
6
58
  const credsPath = `${ normalizedWorkspace }/.creds.yml`;
7
59
  const envPath = `${ normalizedWorkspace }/.env`;
@@ -12,7 +64,10 @@ const copyCredsToEnv = (workspace) => {
12
64
 
13
65
  const credsText = fs.readFileSync(credsPath, 'utf8');
14
66
  const credsFromYml = yaml.parse(credsText);
15
- const newCredsLine = `CREDS=${ JSON.stringify(credsFromYml) }`;
67
+ const credsForEnv = includeCredsPaths?.length
68
+ ? filterCredsByPaths(credsFromYml, includeCredsPaths)
69
+ : credsFromYml;
70
+ const newCredsLine = `CREDS=${ JSON.stringify(credsForEnv) }`;
16
71
 
17
72
  let envFileContents = '';
18
73
  if (fs.existsSync(envPath)) {
@@ -45,4 +100,5 @@ if (require.main === module) {
45
100
 
46
101
  module.exports = {
47
102
  copyCredsToEnv,
103
+ filterCredsByPaths,
48
104
  };
@@ -109,6 +109,7 @@ const deployFunction = async ({
109
109
  groups,
110
110
  before_wrappers,
111
111
  after_wrappers,
112
+ include_creds_paths,
112
113
  source,
113
114
  env,
114
115
  ...gcloudArgs
@@ -197,7 +198,6 @@ const deployFunction = async ({
197
198
  const deployFromHostingYml = async (options = {}) => {
198
199
  const config = getHostConfig(options);
199
200
  setWorkspace(config.workspace);
200
- ensureWorkspaceEnvForDeploy(config.workspace);
201
201
  loadWorkspaceEnv();
202
202
 
203
203
  const hostingConfig = readHostingYml(config.workspace);
@@ -235,9 +235,16 @@ const deployFromHostingYml = async (options = {}) => {
235
235
  return;
236
236
  }
237
237
 
238
+ const functionConfig = functions[functionName];
239
+ const includeCredsPaths = Array.isArray(functionConfig.include_creds_paths)
240
+ ? functionConfig.include_creds_paths
241
+ : undefined;
242
+
243
+ ensureWorkspaceEnvForDeploy(config.workspace, { includeCredsPaths });
244
+
238
245
  await deployFunction({
239
246
  functionName,
240
- functionConfig: functions[functionName],
247
+ functionConfig,
241
248
  googleCloudInfo,
242
249
  workspace: config.workspace,
243
250
  });
@@ -1,8 +1,20 @@
1
1
  const fs = require('fs');
2
2
  const { wrapHostedFunction } = require('./hosting.utils');
3
3
 
4
+ const requirePathForHostedJs = (modulePath) => {
5
+ if (modulePath === './hosting/wrappers.js') {
6
+ return './wrappers.js';
7
+ }
8
+
9
+ if (modulePath.startsWith('./api/')) {
10
+ return `../${ modulePath.slice(2) }`;
11
+ }
12
+
13
+ return modulePath;
14
+ };
15
+
4
16
  const formatResolvedWrapperForHostedJs = ({ modulePath, wrapperName }) => (
5
- `require('${ modulePath }').${ wrapperName }`
17
+ `require('${ requirePathForHostedJs(modulePath) }').${ wrapperName }`
6
18
  );
7
19
 
8
20
  const formatWrapperList = (resolvedWrappers = []) => {
@@ -53,7 +65,7 @@ const generateHostedJs = ({
53
65
  });
54
66
 
55
67
  exportLines.push(
56
- ` ${ hostedName }: wrapHostedFunction(() => require('${ requirePath }'), '${ handlerName }'${ wrappersArg }),`,
68
+ ` ${ hostedName }: wrapHostedFunction(() => require('${ requirePathForHostedJs(requirePath) }'), '${ handlerName }'${ wrappersArg }),`,
57
69
  );
58
70
  }
59
71
 
@@ -77,6 +89,14 @@ const writeHostedJs = ({
77
89
  });
78
90
 
79
91
  fs.mkdirSync(getHostingDir(workspace), { recursive: true });
92
+
93
+ if (fs.existsSync(hostedPath)) {
94
+ const existingContent = fs.readFileSync(hostedPath, 'utf8');
95
+ if (existingContent === content) {
96
+ return hostedPath;
97
+ }
98
+ }
99
+
80
100
  fs.writeFileSync(hostedPath, content);
81
101
  return hostedPath;
82
102
  };
@@ -189,11 +189,12 @@ const readHostingYml = (workspace) => {
189
189
  return hostingConfig;
190
190
  };
191
191
 
192
- const ensureWorkspaceEnvForDeploy = (workspace) => {
192
+ const ensureWorkspaceEnvForDeploy = (workspace, options = {}) => {
193
+ const { includeCredsPaths } = options;
193
194
  const { copyCredsToEnv } = require('./copyCredsToEnv');
194
195
  const envPath = `${ workspace.replace(/\/$/, '') }/.env`;
195
196
 
196
- copyCredsToEnv(workspace);
197
+ copyCredsToEnv(workspace, { includeCredsPaths });
197
198
 
198
199
  if (!fs.existsSync(envPath)) {
199
200
  throw new Error(`Missing .env in workspace: ${ workspace }`);
@@ -0,0 +1,160 @@
1
+ const http = require('http');
2
+ const { createRequire } = require('module');
3
+ const { respondJson, errorToReadable } = require('../server.utils');
4
+ const { getWorkspace, setWorkspace, loadWorkspaceEnv, toAbsolutePath } = require('../api/workspace');
5
+ const { getApiDirs, readCliFlag } = require('../cli');
6
+ const { loadHandlers } = require('../server');
7
+ const {
8
+ readHostingYml,
9
+ resolveHostedHandlersForDeploy,
10
+ wrapHostedFunction,
11
+ } = require('./hosting.utils');
12
+ const { copyCredsToEnv } = require('./copyCredsToEnv');
13
+
14
+ const getConfig = (options = {}) => ({
15
+ port: Number(options.port ?? process.env.PORT ?? 8000),
16
+ workspace: toAbsolutePath(
17
+ options.workspace
18
+ ?? readCliFlag('--workspace')
19
+ ?? process.env.MINERAL_WORKSPACE
20
+ ?? process.cwd(),
21
+ ),
22
+ api_dirs: getApiDirs(options),
23
+ });
24
+
25
+ const handlerByRouteName = (handlers) => {
26
+ const byName = new Map();
27
+
28
+ for (const handler of handlers.values()) {
29
+ byName.set(handler.routeName, handler);
30
+ }
31
+
32
+ return byName;
33
+ };
34
+
35
+ const loadHostedRoutes = (config) => {
36
+ const { functions } = readHostingYml(config.workspace);
37
+ const handlers = loadHandlers({
38
+ ...config,
39
+ host_mode: true,
40
+ });
41
+ const handlersByName = handlerByRouteName(handlers);
42
+ const hostedHandlers = resolveHostedHandlersForDeploy({
43
+ functions,
44
+ workspace: config.workspace,
45
+ handlersByName,
46
+ });
47
+
48
+ const workspaceRequire = createRequire(`${ config.workspace.replace(/\/$/, '') }/package.json`);
49
+ const routes = new Map();
50
+
51
+ for (const hostedHandler of hostedHandlers) {
52
+ const {
53
+ hostedName,
54
+ handlerName,
55
+ resolvedBeforeWrappers = [],
56
+ resolvedAfterWrappers = [],
57
+ requirePath,
58
+ } = hostedHandler;
59
+
60
+ const beforeWrappers = resolvedBeforeWrappers.map(({ modulePath, wrapperName }) => (
61
+ workspaceRequire(modulePath)[wrapperName]
62
+ ));
63
+ const afterWrappers = resolvedAfterWrappers.map(({ modulePath, wrapperName }) => (
64
+ workspaceRequire(modulePath)[wrapperName]
65
+ ));
66
+
67
+ routes.set(
68
+ `/${ hostedName }`,
69
+ wrapHostedFunction(
70
+ () => workspaceRequire(requirePath),
71
+ handlerName,
72
+ {
73
+ beforeWrappers,
74
+ afterWrappers,
75
+ },
76
+ ),
77
+ );
78
+ }
79
+
80
+ return routes;
81
+ };
82
+
83
+ const createHostedPreviewServer = (handlers) => http.createServer(async (req, res) => {
84
+ if (req.method === 'GET' && req.url === '/') {
85
+ respondJson(res, 200, {
86
+ ok: true,
87
+ data: {
88
+ mode: 'hosting_preview',
89
+ routes: [...handlers.keys()].sort(),
90
+ workspace: getWorkspace(),
91
+ },
92
+ });
93
+ return;
94
+ }
95
+
96
+ const route = (req.url || '/').split('?')[0];
97
+ const handler = handlers.get(route);
98
+
99
+ if (!handler) {
100
+ respondJson(res, 404, {
101
+ ok: false,
102
+ error: {
103
+ code: 'NOT_FOUND',
104
+ message: `No hosted handler found for route ${ route }`,
105
+ },
106
+ });
107
+ return;
108
+ }
109
+
110
+ try {
111
+ await handler(req, res);
112
+ } catch (error) {
113
+ if (res.headersSent) {
114
+ return;
115
+ }
116
+
117
+ respondJson(res, 500, {
118
+ ok: false,
119
+ error: {
120
+ code: 'UNHANDLED_ERROR',
121
+ message: 'Unhandled server error.',
122
+ details: errorToReadable(error),
123
+ },
124
+ });
125
+ }
126
+ });
127
+
128
+ const startHostingPreview = (options = {}) => {
129
+ process.env.HOSTED = 'true';
130
+
131
+ const config = getConfig(options);
132
+
133
+ setWorkspace(config.workspace);
134
+ copyCredsToEnv(config.workspace);
135
+ loadWorkspaceEnv();
136
+
137
+ const handlers = loadHostedRoutes(config);
138
+ const server = createHostedPreviewServer(handlers);
139
+
140
+ server.listen(config.port, () => {
141
+ console.log(`Hosting preview running on port ${ config.port }`);
142
+ console.log(`Workspace: ${ config.workspace }`);
143
+ console.log('Hosted routes:');
144
+
145
+ for (const route of [...handlers.keys()].sort()) {
146
+ console.log(route);
147
+ }
148
+ });
149
+
150
+ return server;
151
+ };
152
+
153
+ module.exports = {
154
+ startHostingPreview,
155
+ loadHostedRoutes,
156
+ };
157
+
158
+ if (require.main === module) {
159
+ startHostingPreview();
160
+ }
@@ -22,7 +22,7 @@ const allowCrossOriginCallsAndHandleOptions = async (req, res) => {
22
22
 
23
23
  res.setHeader('Access-Control-Allow-Origin', origin || '*');
24
24
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
25
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, x-api-key, x-wf-token, x-wf-value');
25
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, x-api-key, x-wf-token, x-wf-value, x-wf-app');
26
26
 
27
27
  if (req.method === 'OPTIONS') {
28
28
  res.writeHead(204);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxtware/mineral",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "bin": {
5
5
  "mineral": "bin/mineral.js"
6
6
  },
@@ -10,17 +10,24 @@
10
10
  },
11
11
  "scripts": {
12
12
  "creds_to_env": "node hosting/copyCredsToEnv.js",
13
- "dev": "node --watch --watch-path=./api --watch-path=./server.js --watch-path=./server.utils.js --watch-path=./.creds.yml server.js",
13
+ "dev": "node --watch --watch-path=./api --watch-path=./server.js --watch-path=./server.utils.js --watch-path=./hosting --watch-path=./.creds.yml server.js",
14
+ "hosting_preview": "node --watch --watch-path=./api --watch-path=./hosting/.hosting.yml --watch-path=./hosting/wrappers.js --watch-path=./.creds.yml hosting/hostingPreview.js",
15
+ "tunnel": "ngrok http http://localhost:8000",
14
16
  "new": "node _build_scripts/createNewFunction.js",
15
17
  "serve": "PORT=8100 node server.js",
16
18
  "start": "node server.js",
17
19
  "npm:publish": "node _build_scripts/publish.js"
18
20
  },
19
21
  "dependencies": {
22
+ "@supabase/supabase-js": "^2.55.0",
23
+ "@upstash/redis": "^1.28.4",
20
24
  "csvtojson": "^2.0.14",
21
25
  "dotenv": "^17.2.0",
22
26
  "json-2-csv": "^5.5.9",
23
27
  "xml2js": "^0.6.2",
24
28
  "yaml": "^2.9.0"
29
+ },
30
+ "devDependencies": {
31
+ "ngrok": "^5.0.0-beta.2"
25
32
  }
26
33
  }
package/server.utils.js CHANGED
@@ -258,6 +258,7 @@ const funcApi = (func, config = {}) => {
258
258
 
259
259
  let callArgs = requestContext.args;
260
260
  if (passThroughReq) {
261
+ requestContext.req.body = modifiedBody;
261
262
  callArgs = [requestContext.req];
262
263
  } else if (passThroughBody) {
263
264
  callArgs = [modifiedBody];