wirejs-deploy-amplify-basic 0.1.169 → 0.1.171

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.
@@ -2,6 +2,7 @@ import { env } from 'process';
2
2
  import { LambdaFunctionURLHandler, APIGatewayProxyEventV2 } from 'aws-lambda';
3
3
  import {
4
4
  BackgroundJob,
5
+ CronJob,
5
6
  CookieJar,
6
7
  Context,
7
8
  Endpoint,
@@ -144,6 +145,30 @@ function byPathLength(a: Endpoint, b: Endpoint) {
144
145
  }
145
146
 
146
147
  export const handler: LambdaFunctionURLHandler = async (event) => {
148
+ // Handle EventBridge-scheduled CronJob invocations.
149
+ // EventBridge events have a 'source' field; Lambda Function URL events do not.
150
+ if ((event as any).source === 'wirejs-cron') {
151
+ const cronEvent = event as any;
152
+ const absoluteId: string | undefined = cronEvent['wirejs-cron-id'];
153
+
154
+ if (!absoluteId) {
155
+ console.error('Missing wirejs-cron-id in scheduled event');
156
+ return { statusCode: 400, body: JSON.stringify({ error: 'Missing wirejs-cron-id' }) };
157
+ }
158
+
159
+ console.log('handling scheduled cron job', absoluteId);
160
+
161
+ const entry = (CronJob as any).registeredJobs.get(absoluteId);
162
+ if (!entry || typeof entry.handler !== 'function') {
163
+ console.error('CronJob not found', absoluteId, [...(CronJob as any).registeredJobs.keys()]);
164
+ return { statusCode: 404, body: JSON.stringify({ error: 'CronJob not found' }) };
165
+ }
166
+
167
+ await entry.handler();
168
+
169
+ return { statusCode: 200, body: JSON.stringify({ message: 'CronJob complete' }) };
170
+ }
171
+
147
172
  const calls = event.body ? JSON.parse(event.body) : undefined;
148
173
  const wjsContext = createContext(event);
149
174
  const path = wjsContext?.location.pathname;
@@ -14,6 +14,8 @@ import { NodejsFunction, OutputFormat } from 'aws-cdk-lib/aws-lambda-nodejs';
14
14
  import { Bucket, BlockPublicAccess } from 'aws-cdk-lib/aws-s3';
15
15
  import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb';
16
16
  import { AnyPrincipal, PolicyStatement } from 'aws-cdk-lib/aws-iam';
17
+ import { Rule, RuleTargetInput, Schedule } from 'aws-cdk-lib/aws-events';
18
+ import { LambdaFunction as LambdaFunctionTarget } from 'aws-cdk-lib/aws-events-targets';
17
19
 
18
20
  import { TableDefinition, indexName, DeploymentConfig } from 'wirejs-resources';
19
21
  import { TableIndexes } from './constructs/table-indexes';
@@ -224,6 +226,51 @@ for (const resource of generated) {
224
226
  }
225
227
 
226
228
 
229
+ /**
230
+ * Converts a standard 5-field cron expression to an AWS EventBridge Schedule.
231
+ *
232
+ * Standard cron: "minute hour day-of-month month day-of-week"
233
+ * EventBridge cron: "minute hour day-of-month month day-of-week year"
234
+ * EventBridge additionally requires that day-of-month and day-of-week cannot
235
+ * both be specified at the same time (one must be `?`).
236
+ */
237
+ function cronExpressionToSchedule(expression: string): Schedule {
238
+ const [minute, hour, dayOfMonth, month, dayOfWeek] = expression.trim().split(/\s+/);
239
+ // EventBridge requires exactly one of day-of-month or day-of-week to be '?'.
240
+ // If neither is wildcarded, prefer keeping day-of-month and set weekday to '?'.
241
+ const ebDayOfMonth = dayOfWeek !== '*' && dayOfWeek !== '?' ? '?' : dayOfMonth;
242
+ const ebDayOfWeek = ebDayOfMonth === '?' ? dayOfWeek : '?';
243
+ return Schedule.cron({ minute, hour, day: ebDayOfMonth, month, weekDay: ebDayOfWeek });
244
+ }
245
+
246
+ /**
247
+ * EventBridge rules for CronJobs
248
+ */
249
+ function isCronJob(resource: any): resource is {
250
+ type: 'CronJob';
251
+ options: { absoluteId: string; schedule: string };
252
+ } {
253
+ return resource.type === 'CronJob';
254
+ }
255
+
256
+ for (const resource of generated) {
257
+ if (isCronJob(resource)) {
258
+ const sanitizedId = resource.options.absoluteId.replace(/[^a-zA-Z0-9-_]/g, '_');
259
+ new Rule(backend.stack, `CronJob_${sanitizedId}`, {
260
+ schedule: cronExpressionToSchedule(resource.options.schedule),
261
+ targets: [
262
+ new LambdaFunctionTarget(api, {
263
+ event: RuleTargetInput.fromObject({
264
+ source: 'wirejs-cron',
265
+ 'wirejs-cron-id': resource.options.absoluteId,
266
+ }),
267
+ }),
268
+ ],
269
+ });
270
+ }
271
+ }
272
+
273
+
227
274
  /**
228
275
  * Lambda environment vars
229
276
  */
@@ -305,14 +305,15 @@ async function tryEndpointPath(context, res) {
305
305
  */
306
306
  async function proxyRequest(context, res, targetUrl) {
307
307
  const method = context.httpMethod;
308
+ const parsedUrl = new URL(targetUrl);
308
309
  const headers = {
309
310
  ...context.requestHeaders,
311
+ 'host': parsedUrl.hostname,
310
312
  'x-wirejs-location': context.location.toString()
311
313
  };
312
314
  const body = context.requestBody;
313
315
 
314
316
  try {
315
- const parsedUrl = new URL(targetUrl);
316
317
  const isHttps = parsedUrl.protocol === 'https:';
317
318
  const requester = isHttps ? https : http;
318
319
 
@@ -3,6 +3,6 @@
3
3
  "dependencies": {
4
4
  "jsdom": "^25.0.1",
5
5
  "wirejs-dom": "^1.0.44",
6
- "wirejs-resources": "^0.1.169"
6
+ "wirejs-resources": "^0.1.171"
7
7
  }
8
8
  }
package/dist/index.d.ts CHANGED
@@ -5,4 +5,5 @@ export { LLM } from './services/llm.js';
5
5
  export { DistributedTable } from './resources/distributed-table.js';
6
6
  export { RealtimeService } from './services/realtime.js';
7
7
  export { BackgroundJob } from './resources/background-job.js';
8
+ export { CronJob } from './resources/cron-job.js';
8
9
  export { Endpoint } from './resources/endpoint.js';
package/dist/index.js CHANGED
@@ -14,11 +14,14 @@ import { RealtimeService } from './services/realtime.js';
14
14
  export { RealtimeService } from './services/realtime.js';
15
15
  import { BackgroundJob } from './resources/background-job.js';
16
16
  export { BackgroundJob } from './resources/background-job.js';
17
+ import { CronJob } from './resources/cron-job.js';
18
+ export { CronJob } from './resources/cron-job.js';
17
19
  import { Endpoint } from './resources/endpoint.js';
18
20
  export { Endpoint } from './resources/endpoint.js';
19
21
  // expose resources to other resources that might depend on it.
20
22
  overrides.AuthenticationService = AuthenticationService;
21
23
  overrides.BackgroundJob = BackgroundJob;
24
+ overrides.CronJob = CronJob;
22
25
  overrides.DistributedTable = DistributedTable;
23
26
  overrides.Endpoint = Endpoint;
24
27
  overrides.FileService = FileService;
@@ -0,0 +1,30 @@
1
+ import { Resource, CronJob as BaseCronJob } from 'wirejs-resources';
2
+ export type CronJobDefinition = {
3
+ absoluteId: string;
4
+ schedule: string;
5
+ };
6
+ /**
7
+ * A function that will be executed on a recurring schedule defined by a cron expression.
8
+ *
9
+ * In AWS deployments, the schedule is managed by Amazon EventBridge.
10
+ *
11
+ * A `CronJob` can also invoke an existing BackgroundJob from its handler.
12
+ *
13
+ * ## WARNINGS
14
+ *
15
+ * 1. These jobs execute in a completely different environment that the one that defines it.
16
+ * DO NOT depend on global variables, closures, or other process state that is not lazily
17
+ * created or constructed in-place by the script.
18
+ * 2. In local development, **timeouts are not enforced.** (Yet.)
19
+ */
20
+ export declare class CronJob extends Resource implements Omit<BaseCronJob, '#private'> {
21
+ static registeredJobs: Map<string, {
22
+ schedule: string;
23
+ handler: () => Promise<void>;
24
+ }>;
25
+ readonly schedule: string;
26
+ constructor(scope: Resource | string, id: string, options: {
27
+ schedule: string;
28
+ handler: () => Promise<void>;
29
+ });
30
+ }
@@ -0,0 +1,32 @@
1
+ import { Resource, } from 'wirejs-resources';
2
+ import { addResource } from '../resource-collector.js';
3
+ /**
4
+ * A function that will be executed on a recurring schedule defined by a cron expression.
5
+ *
6
+ * In AWS deployments, the schedule is managed by Amazon EventBridge.
7
+ *
8
+ * A `CronJob` can also invoke an existing BackgroundJob from its handler.
9
+ *
10
+ * ## WARNINGS
11
+ *
12
+ * 1. These jobs execute in a completely different environment that the one that defines it.
13
+ * DO NOT depend on global variables, closures, or other process state that is not lazily
14
+ * created or constructed in-place by the script.
15
+ * 2. In local development, **timeouts are not enforced.** (Yet.)
16
+ */
17
+ export class CronJob extends Resource {
18
+ static registeredJobs = new Map();
19
+ schedule;
20
+ constructor(scope, id, options) {
21
+ super(scope, id);
22
+ this.schedule = options.schedule;
23
+ addResource('CronJob', {
24
+ absoluteId: this.absoluteId,
25
+ schedule: options.schedule,
26
+ });
27
+ CronJob.registeredJobs.set(this.absoluteId, {
28
+ schedule: options.schedule,
29
+ handler: options.handler,
30
+ });
31
+ }
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wirejs-deploy-amplify-basic",
3
- "version": "0.1.169",
3
+ "version": "0.1.171",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -44,7 +44,7 @@
44
44
  "recursive-copy": "^2.0.14",
45
45
  "rimraf": "^6.0.1",
46
46
  "wirejs-dom": "^1.0.44",
47
- "wirejs-resources": "^0.1.169"
47
+ "wirejs-resources": "^0.1.171"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@aws-amplify/backend": "^1.14.0",