@tribe-nest/forge 1.18.0 → 1.20.0

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/server/jobs.ts +31 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tribe-nest/forge",
3
- "version": "1.18.0",
3
+ "version": "1.20.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -24,9 +24,20 @@ interface AppJobsStub {
24
24
  * and what happened on the LAST delivery attempt. Because jobs fail silently (no
25
25
  * logs / dead-letter), this is how you SEE why one didn't run:
26
26
  * - `lastDispatchAt` null while `alarmAt` is in the past → the alarm isn't firing;
27
- * - `lastError` starting "dispatch threw:" → the callback never reached your app;
27
+ * - `lastError` starting "runner unreachable:" → a platform-side fault, not yours;
28
+ * - `lastDispatchStatus` "404" + "no dispatch target" → the platform has no
29
+ * record of where to send your runs; republish the app;
30
+ * - `lastDispatchStatus` "502" → your Worker isn't deployed;
28
31
  * - `lastDispatchStatus` "401" → your app was reached but rejected the token
29
- * (the dispatch-worker's APP_SYNC_SECRET ≠ the backend JWT_SECRET).
32
+ * (the dispatch-worker's APP_SYNC_SECRET ≠ the backend JWT_SECRET);
33
+ * - `lastDispatchStatus` "404" + "unknown job" → the run landed but no handler
34
+ * in your job map has that name.
35
+ *
36
+ * For "my one-time job never ran", read `deadLetters` first. `queueDepth` on its
37
+ * own tells you almost nothing: `enqueueAppJob(..., delayMs: 0)` causes the run
38
+ * to be dispatched and the row deleted within seconds, so depth reads 0 whether
39
+ * the job succeeded or was never enqueued at all. A job that keeps failing
40
+ * retries for ~15 minutes and then appears in `deadLetters` with its error.
30
41
  */
31
42
  export interface AppJobsStatus {
32
43
  appId: string | null;
@@ -34,7 +45,13 @@ export interface AppJobsStatus {
34
45
  alarmAt: number | null;
35
46
  schedules: Array<{ name: string; intervalMs: number | null; cron: string | null; nextRun: number }>;
36
47
  queueDepth: number;
48
+ /** Names currently queued with their next attempt time. */
49
+ queued: Array<{ name: string; runAt: number; attempts: number }>;
50
+ /** One-time jobs that exhausted their retries and were given up on, newest first. */
51
+ deadLetters: Array<{ name: string; attempts: number; failedAt: number; error: string | null }>;
37
52
  lastDispatchAt: number | null;
53
+ /** Which job the last delivery attempt was for — all jobs share one error slot. */
54
+ lastDispatchJob: string | null;
38
55
  lastDispatchScript: string | null;
39
56
  lastDispatchStatus: string | null;
40
57
  lastError: string | null;
@@ -68,10 +85,18 @@ export type JobHandler = (payload: unknown, env: unknown, ctx: JobContext) => Pr
68
85
  export interface AppJobsConfig {
69
86
  /** The app id (VITE_APP_ID). */
70
87
  appId: string;
71
- /** This Worker's script name in the dispatch namespace (VITE_APP_SCRIPT). */
72
- script: string;
73
88
  /** The TribeNest public API base (VITE_API_URL) — used to verify the callback token. */
74
89
  apiUrl: string;
90
+ /**
91
+ * @deprecated No longer needed — leave it unset in new apps.
92
+ *
93
+ * This Worker's script name in the dispatch namespace (VITE_APP_SCRIPT). Your
94
+ * app's address is a PLATFORM fact: it is recorded at publish and resolved by
95
+ * the job runtime from your `appId`, so an app no longer reports where it
96
+ * lives. Still accepted (and forwarded as a fallback hint) so apps published
97
+ * before that change keep dispatching without a rebuild.
98
+ */
99
+ script?: string;
75
100
  }
76
101
 
77
102
  const stub = (env: AppJobsEnv, appId: string): AppJobsStub => env.JOBS.get(env.JOBS.idFromName(appId));
@@ -93,7 +118,7 @@ function scheduleHash(schedules: JobSchedule[]): string {
93
118
  * one, a removed one is pruned.
94
119
  */
95
120
  export async function reconcileAppJobs(env: AppJobsEnv, cfg: AppJobsConfig, schedules: JobSchedule[]): Promise<void> {
96
- await stub(env, cfg.appId).reconcile(cfg.script, cfg.appId, schedules, scheduleHash(schedules));
121
+ await stub(env, cfg.appId).reconcile(cfg.script ?? "", cfg.appId, schedules, scheduleHash(schedules));
97
122
  }
98
123
 
99
124
  /** Enqueue a durable one-time job (survives redeploys, retried on failure). */
@@ -104,7 +129,7 @@ export async function enqueueAppJob(
104
129
  payload?: unknown,
105
130
  delayMs = 0,
106
131
  ): Promise<void> {
107
- await stub(env, cfg.appId).enqueue(cfg.script, cfg.appId, name, payload ?? null, delayMs);
132
+ await stub(env, cfg.appId).enqueue(cfg.script ?? "", cfg.appId, name, payload ?? null, delayMs);
108
133
  }
109
134
 
110
135
  /**