@lowdefy/server-dev 0.0.0-experimental-20260908113207 → 0.0.0-experimental-20260908154018

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowdefy/server-dev",
3
- "version": "0.0.0-experimental-20260908113207",
3
+ "version": "0.0.0-experimental-20260908154018",
4
4
  "license": "Apache-2.0",
5
5
  "description": "",
6
6
  "homepage": "https://lowdefy.com",
@@ -45,40 +45,40 @@
45
45
  "@hono/mcp": "0.3.0",
46
46
  "@hono/node-server": "2.0.4",
47
47
  "@hono/vite-dev-server": "0.26.0",
48
- "@lowdefy/actions-core": "0.0.0-experimental-20260908113207",
49
- "@lowdefy/api": "0.0.0-experimental-20260908113207",
50
- "@lowdefy/block-utils": "0.0.0-experimental-20260908113207",
51
- "@lowdefy/blocks-aggrid": "0.0.0-experimental-20260908113207",
52
- "@lowdefy/blocks-antd": "0.0.0-experimental-20260908113207",
53
- "@lowdefy/blocks-antd-x": "0.0.0-experimental-20260908113207",
54
- "@lowdefy/blocks-basic": "0.0.0-experimental-20260908113207",
55
- "@lowdefy/blocks-captcha": "0.0.0-experimental-20260908113207",
56
- "@lowdefy/blocks-echarts": "0.0.0-experimental-20260908113207",
57
- "@lowdefy/blocks-loaders": "0.0.0-experimental-20260908113207",
58
- "@lowdefy/blocks-markdown": "0.0.0-experimental-20260908113207",
59
- "@lowdefy/blocks-tiptap": "0.0.0-experimental-20260908113207",
60
- "@lowdefy/build": "0.0.0-experimental-20260908113207",
61
- "@lowdefy/client": "0.0.0-experimental-20260908113207",
62
- "@lowdefy/connection-axios-http": "0.0.0-experimental-20260908113207",
63
- "@lowdefy/connection-mongodb": "0.0.0-experimental-20260908113207",
64
- "@lowdefy/docs-content": "0.0.0-experimental-20260908113207",
65
- "@lowdefy/e2e-utils": "0.0.0-experimental-20260908113207",
66
- "@lowdefy/engine": "0.0.0-experimental-20260908113207",
67
- "@lowdefy/errors": "0.0.0-experimental-20260908113207",
68
- "@lowdefy/helpers": "0.0.0-experimental-20260908113207",
69
- "@lowdefy/layout": "0.0.0-experimental-20260908113207",
70
- "@lowdefy/logger": "0.0.0-experimental-20260908113207",
71
- "@lowdefy/node-utils": "0.0.0-experimental-20260908113207",
72
- "@lowdefy/operators-change-case": "0.0.0-experimental-20260908113207",
73
- "@lowdefy/operators-dayjs": "0.0.0-experimental-20260908113207",
74
- "@lowdefy/operators-diff": "0.0.0-experimental-20260908113207",
75
- "@lowdefy/operators-js": "0.0.0-experimental-20260908113207",
76
- "@lowdefy/operators-mql": "0.0.0-experimental-20260908113207",
77
- "@lowdefy/operators-nunjucks": "0.0.0-experimental-20260908113207",
78
- "@lowdefy/operators-uuid": "0.0.0-experimental-20260908113207",
79
- "@lowdefy/operators-yaml": "0.0.0-experimental-20260908113207",
80
- "@lowdefy/plugin-better-auth": "0.0.0-experimental-20260908113207",
81
- "@lowdefy/websockets-core": "0.0.0-experimental-20260908113207",
48
+ "@lowdefy/actions-core": "0.0.0-experimental-20260908154018",
49
+ "@lowdefy/api": "0.0.0-experimental-20260908154018",
50
+ "@lowdefy/block-utils": "0.0.0-experimental-20260908154018",
51
+ "@lowdefy/blocks-aggrid": "0.0.0-experimental-20260908154018",
52
+ "@lowdefy/blocks-antd": "0.0.0-experimental-20260908154018",
53
+ "@lowdefy/blocks-antd-x": "0.0.0-experimental-20260908154018",
54
+ "@lowdefy/blocks-basic": "0.0.0-experimental-20260908154018",
55
+ "@lowdefy/blocks-captcha": "0.0.0-experimental-20260908154018",
56
+ "@lowdefy/blocks-echarts": "0.0.0-experimental-20260908154018",
57
+ "@lowdefy/blocks-loaders": "0.0.0-experimental-20260908154018",
58
+ "@lowdefy/blocks-markdown": "0.0.0-experimental-20260908154018",
59
+ "@lowdefy/blocks-tiptap": "0.0.0-experimental-20260908154018",
60
+ "@lowdefy/build": "0.0.0-experimental-20260908154018",
61
+ "@lowdefy/client": "0.0.0-experimental-20260908154018",
62
+ "@lowdefy/connection-axios-http": "0.0.0-experimental-20260908154018",
63
+ "@lowdefy/connection-mongodb": "0.0.0-experimental-20260908154018",
64
+ "@lowdefy/docs-content": "0.0.0-experimental-20260908154018",
65
+ "@lowdefy/e2e-utils": "0.0.0-experimental-20260908154018",
66
+ "@lowdefy/engine": "0.0.0-experimental-20260908154018",
67
+ "@lowdefy/errors": "0.0.0-experimental-20260908154018",
68
+ "@lowdefy/helpers": "0.0.0-experimental-20260908154018",
69
+ "@lowdefy/layout": "0.0.0-experimental-20260908154018",
70
+ "@lowdefy/logger": "0.0.0-experimental-20260908154018",
71
+ "@lowdefy/node-utils": "0.0.0-experimental-20260908154018",
72
+ "@lowdefy/operators-change-case": "0.0.0-experimental-20260908154018",
73
+ "@lowdefy/operators-dayjs": "0.0.0-experimental-20260908154018",
74
+ "@lowdefy/operators-diff": "0.0.0-experimental-20260908154018",
75
+ "@lowdefy/operators-js": "0.0.0-experimental-20260908154018",
76
+ "@lowdefy/operators-mql": "0.0.0-experimental-20260908154018",
77
+ "@lowdefy/operators-nunjucks": "0.0.0-experimental-20260908154018",
78
+ "@lowdefy/operators-uuid": "0.0.0-experimental-20260908154018",
79
+ "@lowdefy/operators-yaml": "0.0.0-experimental-20260908154018",
80
+ "@lowdefy/plugin-better-auth": "0.0.0-experimental-20260908154018",
81
+ "@lowdefy/websockets-core": "0.0.0-experimental-20260908154018",
82
82
  "@modelcontextprotocol/sdk": "1.29.0",
83
83
  "@tailwindcss/postcss": "4.3.1",
84
84
  "@vitejs/plugin-react": "6.0.2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowdefy/server-dev",
3
- "version": "0.0.0-experimental-20260908113207",
3
+ "version": "0.0.0-experimental-20260908154018",
4
4
  "license": "Apache-2.0",
5
5
  "description": "",
6
6
  "homepage": "https://lowdefy.com",
@@ -51,40 +51,40 @@
51
51
  "@hono/mcp": "0.3.0",
52
52
  "@hono/node-server": "2.0.4",
53
53
  "@hono/vite-dev-server": "0.26.0",
54
- "@lowdefy/actions-core": "0.0.0-experimental-20260908113207",
55
- "@lowdefy/api": "0.0.0-experimental-20260908113207",
56
- "@lowdefy/block-utils": "0.0.0-experimental-20260908113207",
57
- "@lowdefy/blocks-aggrid": "0.0.0-experimental-20260908113207",
58
- "@lowdefy/blocks-antd": "0.0.0-experimental-20260908113207",
59
- "@lowdefy/blocks-antd-x": "0.0.0-experimental-20260908113207",
60
- "@lowdefy/blocks-basic": "0.0.0-experimental-20260908113207",
61
- "@lowdefy/blocks-captcha": "0.0.0-experimental-20260908113207",
62
- "@lowdefy/blocks-echarts": "0.0.0-experimental-20260908113207",
63
- "@lowdefy/blocks-loaders": "0.0.0-experimental-20260908113207",
64
- "@lowdefy/blocks-markdown": "0.0.0-experimental-20260908113207",
65
- "@lowdefy/blocks-tiptap": "0.0.0-experimental-20260908113207",
66
- "@lowdefy/build": "0.0.0-experimental-20260908113207",
67
- "@lowdefy/client": "0.0.0-experimental-20260908113207",
68
- "@lowdefy/connection-axios-http": "0.0.0-experimental-20260908113207",
69
- "@lowdefy/connection-mongodb": "0.0.0-experimental-20260908113207",
70
- "@lowdefy/docs-content": "0.0.0-experimental-20260908113207",
71
- "@lowdefy/e2e-utils": "0.0.0-experimental-20260908113207",
72
- "@lowdefy/engine": "0.0.0-experimental-20260908113207",
73
- "@lowdefy/errors": "0.0.0-experimental-20260908113207",
74
- "@lowdefy/helpers": "0.0.0-experimental-20260908113207",
75
- "@lowdefy/layout": "0.0.0-experimental-20260908113207",
76
- "@lowdefy/logger": "0.0.0-experimental-20260908113207",
77
- "@lowdefy/node-utils": "0.0.0-experimental-20260908113207",
78
- "@lowdefy/operators-change-case": "0.0.0-experimental-20260908113207",
79
- "@lowdefy/operators-dayjs": "0.0.0-experimental-20260908113207",
80
- "@lowdefy/operators-diff": "0.0.0-experimental-20260908113207",
81
- "@lowdefy/operators-js": "0.0.0-experimental-20260908113207",
82
- "@lowdefy/operators-mql": "0.0.0-experimental-20260908113207",
83
- "@lowdefy/operators-nunjucks": "0.0.0-experimental-20260908113207",
84
- "@lowdefy/operators-uuid": "0.0.0-experimental-20260908113207",
85
- "@lowdefy/operators-yaml": "0.0.0-experimental-20260908113207",
86
- "@lowdefy/plugin-better-auth": "0.0.0-experimental-20260908113207",
87
- "@lowdefy/websockets-core": "0.0.0-experimental-20260908113207",
54
+ "@lowdefy/actions-core": "0.0.0-experimental-20260908154018",
55
+ "@lowdefy/api": "0.0.0-experimental-20260908154018",
56
+ "@lowdefy/block-utils": "0.0.0-experimental-20260908154018",
57
+ "@lowdefy/blocks-aggrid": "0.0.0-experimental-20260908154018",
58
+ "@lowdefy/blocks-antd": "0.0.0-experimental-20260908154018",
59
+ "@lowdefy/blocks-antd-x": "0.0.0-experimental-20260908154018",
60
+ "@lowdefy/blocks-basic": "0.0.0-experimental-20260908154018",
61
+ "@lowdefy/blocks-captcha": "0.0.0-experimental-20260908154018",
62
+ "@lowdefy/blocks-echarts": "0.0.0-experimental-20260908154018",
63
+ "@lowdefy/blocks-loaders": "0.0.0-experimental-20260908154018",
64
+ "@lowdefy/blocks-markdown": "0.0.0-experimental-20260908154018",
65
+ "@lowdefy/blocks-tiptap": "0.0.0-experimental-20260908154018",
66
+ "@lowdefy/build": "0.0.0-experimental-20260908154018",
67
+ "@lowdefy/client": "0.0.0-experimental-20260908154018",
68
+ "@lowdefy/connection-axios-http": "0.0.0-experimental-20260908154018",
69
+ "@lowdefy/connection-mongodb": "0.0.0-experimental-20260908154018",
70
+ "@lowdefy/docs-content": "0.0.0-experimental-20260908154018",
71
+ "@lowdefy/e2e-utils": "0.0.0-experimental-20260908154018",
72
+ "@lowdefy/engine": "0.0.0-experimental-20260908154018",
73
+ "@lowdefy/errors": "0.0.0-experimental-20260908154018",
74
+ "@lowdefy/helpers": "0.0.0-experimental-20260908154018",
75
+ "@lowdefy/layout": "0.0.0-experimental-20260908154018",
76
+ "@lowdefy/logger": "0.0.0-experimental-20260908154018",
77
+ "@lowdefy/node-utils": "0.0.0-experimental-20260908154018",
78
+ "@lowdefy/operators-change-case": "0.0.0-experimental-20260908154018",
79
+ "@lowdefy/operators-dayjs": "0.0.0-experimental-20260908154018",
80
+ "@lowdefy/operators-diff": "0.0.0-experimental-20260908154018",
81
+ "@lowdefy/operators-js": "0.0.0-experimental-20260908154018",
82
+ "@lowdefy/operators-mql": "0.0.0-experimental-20260908154018",
83
+ "@lowdefy/operators-nunjucks": "0.0.0-experimental-20260908154018",
84
+ "@lowdefy/operators-uuid": "0.0.0-experimental-20260908154018",
85
+ "@lowdefy/operators-yaml": "0.0.0-experimental-20260908154018",
86
+ "@lowdefy/plugin-better-auth": "0.0.0-experimental-20260908154018",
87
+ "@lowdefy/websockets-core": "0.0.0-experimental-20260908154018",
88
88
  "@modelcontextprotocol/sdk": "1.29.0",
89
89
  "@tailwindcss/postcss": "4.3.1",
90
90
  "@vitejs/plugin-react": "6.0.2",
package/src/app.js CHANGED
@@ -25,6 +25,7 @@ import authMiddleware from './routes/auth.js';
25
25
  import clientErrorHandler from './routes/clientError.js';
26
26
  import createErrorHandler from './middleware/errorHandler.js';
27
27
  import createLogger from '../lib/server/log/createLogger.js';
28
+ import cronForwardHandler from './routes/cronForward.js';
28
29
  import cronHandler from './routes/cron.js';
29
30
  import detachedHandler from './routes/detached.js';
30
31
  import devInspectHandler from './routes/devInspect.js';
@@ -184,6 +185,7 @@ function createApp() {
184
185
  // cap bodies at 10 MiB to match the agent route.
185
186
  app.all('/api/endpoints/*', bodyLimit({ maxSize: 10 * 1024 * 1024 }), endpointsHandler);
186
187
  app.get('/api/cron/*', cronHandler);
188
+ app.get('/api/cron-forward/*', cronForwardHandler);
187
189
  app.post('/api/detached/*', detachedHandler);
188
190
  app.all('/api/client-error', clientErrorHandler);
189
191
  app.all('/api/usage', usageHandler);
@@ -21,7 +21,9 @@ import getPathSegments from '../lib/getPathSegments.js';
21
21
  // Triggered by Vercel Cron in production: an HTTP GET to /api/cron/<endpointId>. Available in dev so
22
22
  // scheduled endpoints can be triggered locally with curl. Vercel auto-sends the value of the project
23
23
  // env var named exactly CRON_SECRET as `Authorization: Bearer <value>`, and the firing cron
24
- // expression in the `x-vercel-cron-schedule` header. Fails closed if CRON_SECRET is unset.
24
+ // expression in the `x-vercel-cron-schedule` header. Fails closed if CRON_SECRET is unset. A request
25
+ // forwarded from the production deployment of another environment (see cronForward.js) also names
26
+ // the environment whose schedules apply in `x-lowdefy-cron-environment`.
25
27
  async function cronHandler(c) {
26
28
  if (c.req.method !== 'GET') {
27
29
  // A wrong-method request is client-caused: answer 405 rather than raising a
@@ -38,8 +40,9 @@ async function cronHandler(c) {
38
40
 
39
41
  const endpointId = getPathSegments(c, '/api/cron/').join('/');
40
42
  const cron = c.req.header('x-vercel-cron-schedule');
41
- context.logger.info({ event: 'call_cron_endpoint', endpointId, cron });
42
- const response = await runScheduledEndpoint(context, { endpointId, cron });
43
+ const environment = c.req.header('x-lowdefy-cron-environment');
44
+ context.logger.info({ event: 'call_cron_endpoint', endpointId, cron, environment });
45
+ const response = await runScheduledEndpoint(context, { endpointId, cron, environment });
43
46
  return c.json(response);
44
47
  }
45
48
 
@@ -0,0 +1,49 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import { forwardScheduledEndpoint } from '@lowdefy/api';
18
+
19
+ import getPathSegments from '../lib/getPathSegments.js';
20
+
21
+ // Triggered by Vercel Cron on the production deployment: an HTTP GET to
22
+ // /api/cron-forward/<environment>/<endpointId>, registered by `lowdefy vercel-output` for the
23
+ // schedules of every environment declared with a url in config.cron.environments. Vercel fires crons
24
+ // only on production, so production pings that environment's own /api/cron/<endpointId> (with the
25
+ // environment's CRON_SECRET) and answers immediately. Same transport auth as cron (CRON_SECRET,
26
+ // fails closed).
27
+ async function cronForwardHandler(c) {
28
+ if (c.req.method !== 'GET') {
29
+ // A wrong-method request is client-caused: answer 405 rather than raising a
30
+ // fault that would be logged at error level and answered with a 500.
31
+ return c.json({ error: 'Method not allowed.' }, 405);
32
+ }
33
+ const context = c.get('lowdefyContext');
34
+
35
+ const secret = process.env.CRON_SECRET;
36
+ if (!secret || c.req.header('authorization') !== `Bearer ${secret}`) {
37
+ context.logger.warn({ event: 'cron_forward_unauthorized' });
38
+ return c.json({ error: 'Unauthorized' }, 401);
39
+ }
40
+
41
+ const [environment, ...endpointSegments] = getPathSegments(c, '/api/cron-forward/');
42
+ const endpointId = endpointSegments.join('/');
43
+ const cron = c.req.header('x-vercel-cron-schedule');
44
+ context.logger.info({ event: 'call_cron_forward', environment, endpointId, cron });
45
+ const response = await forwardScheduledEndpoint(context, { environment, endpointId, cron });
46
+ return c.json(response);
47
+ }
48
+
49
+ export default cronForwardHandler;