@prenta/core 8.0.0 → 8.1.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 (52) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/api/routes/scheduling.d.ts.map +1 -1
  3. package/dist/api/routes/scheduling.js +6 -47
  4. package/dist/api/routes/scheduling.js.map +1 -1
  5. package/dist/content-health/cron.d.ts +16 -0
  6. package/dist/content-health/cron.d.ts.map +1 -1
  7. package/dist/content-health/cron.js +18 -4
  8. package/dist/content-health/cron.js.map +1 -1
  9. package/dist/db/model-types.d.ts +15 -0
  10. package/dist/db/model-types.d.ts.map +1 -1
  11. package/dist/generated/browser.d.ts +8 -0
  12. package/dist/generated/client.d.ts +8 -0
  13. package/dist/generated/internal/class.d.ts +11 -0
  14. package/dist/generated/internal/class.js +4 -4
  15. package/dist/generated/internal/prismaNamespace.d.ts +99 -1
  16. package/dist/generated/internal/prismaNamespace.js +22 -1
  17. package/dist/generated/internal/prismaNamespaceBrowser.d.ts +23 -0
  18. package/dist/generated/internal/prismaNamespaceBrowser.js +22 -1
  19. package/dist/generated/models/Job.d.ts +1243 -0
  20. package/dist/generated/models/Job.js +1 -0
  21. package/dist/generated/models.d.ts +1 -0
  22. package/dist/jobs/handlers.d.ts +4 -0
  23. package/dist/jobs/handlers.d.ts.map +1 -0
  24. package/dist/jobs/handlers.js +95 -0
  25. package/dist/jobs/handlers.js.map +1 -0
  26. package/dist/jobs/memory-store.d.ts +13 -0
  27. package/dist/jobs/memory-store.d.ts.map +1 -0
  28. package/dist/jobs/memory-store.js +55 -0
  29. package/dist/jobs/memory-store.js.map +1 -0
  30. package/dist/jobs/prisma-store.d.ts +13 -0
  31. package/dist/jobs/prisma-store.d.ts.map +1 -0
  32. package/dist/jobs/prisma-store.js +113 -0
  33. package/dist/jobs/prisma-store.js.map +1 -0
  34. package/dist/jobs/queue.d.ts +17 -0
  35. package/dist/jobs/queue.d.ts.map +1 -0
  36. package/dist/jobs/queue.js +134 -0
  37. package/dist/jobs/queue.js.map +1 -0
  38. package/dist/jobs/routes.d.ts +10 -0
  39. package/dist/jobs/routes.d.ts.map +1 -0
  40. package/dist/jobs/routes.js +61 -0
  41. package/dist/jobs/routes.js.map +1 -0
  42. package/dist/jobs/seo-scan-tick.d.ts +12 -0
  43. package/dist/jobs/seo-scan-tick.d.ts.map +1 -0
  44. package/dist/jobs/seo-scan-tick.js +41 -0
  45. package/dist/jobs/seo-scan-tick.js.map +1 -0
  46. package/dist/jobs/types.d.ts +52 -0
  47. package/dist/jobs/types.d.ts.map +1 -0
  48. package/dist/jobs/types.js +2 -0
  49. package/dist/jobs/types.js.map +1 -0
  50. package/package.json +1 -1
  51. package/prisma/migrations/0022_jobs/migration.sql +20 -0
  52. package/prisma/schema.prisma +22 -0
@@ -0,0 +1,41 @@
1
+ import { AuditAlreadyRunningError, frontEndContentCollectionTypes, runAndPersistAudit, sweepStaleAuditRuns, } from '../seo/audit-runner.js';
2
+ import { maybeSendSeoDigest } from '../seo/digest.js';
3
+ import { getPrentaConfig } from '../config/runtime.js';
4
+ /**
5
+ * One SEO audit + digest attempt. Failures stay in the result so a retry
6
+ * policy can see them, and a thrown audit error does not discard the digest.
7
+ */
8
+ export async function runSeoScanTick(db) {
9
+ let swept = 0;
10
+ try {
11
+ swept = await sweepStaleAuditRuns(db);
12
+ }
13
+ catch {
14
+ // Sweep is best-effort hygiene — never blocks the audit.
15
+ }
16
+ let audit;
17
+ try {
18
+ const collectionTypes = frontEndContentCollectionTypes(getPrentaConfig());
19
+ const run = await runAndPersistAudit(db, { startedById: null, collectionTypes });
20
+ audit = {
21
+ runId: run.id,
22
+ status: run.status,
23
+ siteScore: run.siteScoreAfter,
24
+ issuesFound: run.issuesFound,
25
+ pagesScanned: run.pagesScanned,
26
+ };
27
+ }
28
+ catch (err) {
29
+ const message = String(err instanceof Error ? err.message : err);
30
+ audit = err instanceof AuditAlreadyRunningError ? { skipped: message } : { error: message };
31
+ }
32
+ let digest;
33
+ try {
34
+ digest = await maybeSendSeoDigest(db);
35
+ }
36
+ catch (err) {
37
+ digest = { sent: false, error: String(err instanceof Error ? err.message : err) };
38
+ }
39
+ return { swept, audit, digest };
40
+ }
41
+ //# sourceMappingURL=seo-scan-tick.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo-scan-tick.js","sourceRoot":"","sources":["../../src/jobs/seo-scan-tick.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,8BAA8B,EAC9B,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,kBAAkB,EAAyB,MAAM,kBAAkB,CAAA;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAGtD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EAAY;IAK/C,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,mBAAmB,CAAC,EAAE,CAAC,CAAA;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,yDAAyD;IAC3D,CAAC;IAED,IAAI,KAA8B,CAAA;IAClC,IAAI,CAAC;QACH,MAAM,eAAe,GAAG,8BAA8B,CAAC,eAAe,EAAE,CAAC,CAAA;QACzE,MAAM,GAAG,GAAG,MAAM,kBAAkB,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAA;QAChF,KAAK,GAAG;YACN,KAAK,EAAE,GAAG,CAAC,EAAE;YACb,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,SAAS,EAAE,GAAG,CAAC,cAAc;YAC7B,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,YAAY,EAAE,GAAG,CAAC,YAAY;SAC/B,CAAA;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;QAChE,KAAK,GAAG,GAAG,YAAY,wBAAwB,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAA;IAC7F,CAAC;IAED,IAAI,MAAwB,CAAA;IAC5B,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,kBAAkB,CAAC,EAAE,CAAC,CAAA;IACvC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAA;IACnF,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;AACjC,CAAC"}
@@ -0,0 +1,52 @@
1
+ export type JobStatus = 'queued' | 'running' | 'succeeded' | 'dead';
2
+ export interface JobProgress {
3
+ completed: number;
4
+ total: number;
5
+ message?: string;
6
+ }
7
+ export interface JobRecord {
8
+ id: string;
9
+ type: string;
10
+ payload: Record<string, unknown>;
11
+ status: JobStatus;
12
+ attempts: number;
13
+ maxAttempts: number;
14
+ runAt: Date;
15
+ lockedAt: Date | null;
16
+ lastError: string | null;
17
+ progress: JobProgress;
18
+ createdAt: Date;
19
+ updatedAt: Date;
20
+ }
21
+ interface JobHandlerContext {
22
+ now: Date;
23
+ deadline: Date;
24
+ }
25
+ type JobHandlerResult = {
26
+ done: true;
27
+ progress?: JobProgress;
28
+ } | {
29
+ done: false;
30
+ progress: JobProgress;
31
+ payload?: Record<string, unknown>;
32
+ };
33
+ export type JobHandler = (job: JobRecord, ctx: JobHandlerContext) => Promise<JobHandlerResult>;
34
+ export interface JobStore {
35
+ insert(job: JobRecord): Promise<void>;
36
+ /** A queued or running job of this type, if one exists. */
37
+ findActive(type: string): Promise<JobRecord | null>;
38
+ /** Oldest queued job whose runAt has passed. Marks it running. */
39
+ claimNext(now: Date): Promise<JobRecord | null>;
40
+ /** Running jobs locked before `cutoff` (the worker was killed mid-slice). */
41
+ findStaleRunning(cutoff: Date): Promise<JobRecord[]>;
42
+ save(job: JobRecord): Promise<void>;
43
+ list(limit: number): Promise<JobRecord[]>;
44
+ }
45
+ export interface QueueTickResult {
46
+ succeeded: number;
47
+ continued: number;
48
+ retried: number;
49
+ dead: number;
50
+ }
51
+ export {};
52
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/jobs/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM,CAAA;AAEnE,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,MAAM,EAAE,SAAS,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,IAAI,CAAA;IACX,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAA;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,EAAE,WAAW,CAAA;IACrB,SAAS,EAAE,IAAI,CAAA;IACf,SAAS,EAAE,IAAI,CAAA;CAChB;AAED,UAAU,iBAAiB;IACzB,GAAG,EAAE,IAAI,CAAA;IACT,QAAQ,EAAE,IAAI,CAAA;CACf;AAED,KAAK,gBAAgB,GACjB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,WAAW,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,WAAW,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAA;AAE7E,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,iBAAiB,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAA;AAE9F,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,2DAA2D;IAC3D,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAA;IACnD,kEAAkE;IAClE,SAAS,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAA;IAC/C,6EAA6E;IAC7E,gBAAgB,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAA;IACpD,IAAI,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAA;CAC1C;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/jobs/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prenta/core",
3
- "version": "8.0.0",
3
+ "version": "8.1.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,20 @@
1
+ -- Job queue for SEO audit, content health, and batch AI (#444).
2
+
3
+ CREATE TABLE "prenta_jobs" (
4
+ "id" TEXT NOT NULL,
5
+ "type" TEXT NOT NULL,
6
+ "payload" JSONB NOT NULL,
7
+ "status" TEXT NOT NULL,
8
+ "attempts" INTEGER NOT NULL DEFAULT 0,
9
+ "maxAttempts" INTEGER NOT NULL DEFAULT 3,
10
+ "runAt" TIMESTAMP(3) NOT NULL,
11
+ "lockedAt" TIMESTAMP(3),
12
+ "lastError" TEXT,
13
+ "progress" JSONB NOT NULL,
14
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
15
+ "updatedAt" TIMESTAMP(3) NOT NULL,
16
+
17
+ CONSTRAINT "prenta_jobs_pkey" PRIMARY KEY ("id")
18
+ );
19
+
20
+ CREATE INDEX "prenta_jobs_status_runAt_idx" ON "prenta_jobs"("status", "runAt");
@@ -1116,3 +1116,25 @@ model DocumentNotification {
1116
1116
  @@index([commentId])
1117
1117
  @@map("prenta_document_notifications")
1118
1118
  }
1119
+
1120
+ /// Background work for SEO audit, content health, and batch AI.
1121
+ /// `status` is queued | running | succeeded | dead. A dead row is the
1122
+ /// dead-letter: it is not claimed again. Ids are minted by the queue so a
1123
+ /// stub database that ignores `create` still returns a job id.
1124
+ model Job {
1125
+ id String @id
1126
+ type String
1127
+ payload Json
1128
+ status String
1129
+ attempts Int @default(0)
1130
+ maxAttempts Int @default(3)
1131
+ runAt DateTime
1132
+ lockedAt DateTime?
1133
+ lastError String?
1134
+ progress Json
1135
+ createdAt DateTime @default(now())
1136
+ updatedAt DateTime @updatedAt
1137
+
1138
+ @@index([status, runAt])
1139
+ @@map("prenta_jobs")
1140
+ }