@codeandink/payload-ink 0.0.7-alpha.1

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 (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +64 -0
  3. package/dist/audits/freshness.d.ts +11 -0
  4. package/dist/audits/freshness.d.ts.map +1 -0
  5. package/dist/audits/freshness.js +89 -0
  6. package/dist/audits/freshness.js.map +1 -0
  7. package/dist/audits/seo.d.ts +4 -0
  8. package/dist/audits/seo.d.ts.map +1 -0
  9. package/dist/audits/seo.js +128 -0
  10. package/dist/audits/seo.js.map +1 -0
  11. package/dist/client.d.ts +3 -0
  12. package/dist/client.d.ts.map +1 -0
  13. package/dist/client.js +18 -0
  14. package/dist/client.js.map +1 -0
  15. package/dist/collections/Actions.d.ts +3 -0
  16. package/dist/collections/Actions.d.ts.map +1 -0
  17. package/dist/collections/Actions.js +56 -0
  18. package/dist/collections/Actions.js.map +1 -0
  19. package/dist/collections/Audits.d.ts +3 -0
  20. package/dist/collections/Audits.d.ts.map +1 -0
  21. package/dist/collections/Audits.js +69 -0
  22. package/dist/collections/Audits.js.map +1 -0
  23. package/dist/collections/Insights.d.ts +3 -0
  24. package/dist/collections/Insights.d.ts.map +1 -0
  25. package/dist/collections/Insights.js +99 -0
  26. package/dist/collections/Insights.js.map +1 -0
  27. package/dist/collections/OnboardingProfile.d.ts +3 -0
  28. package/dist/collections/OnboardingProfile.d.ts.map +1 -0
  29. package/dist/collections/OnboardingProfile.js +174 -0
  30. package/dist/collections/OnboardingProfile.js.map +1 -0
  31. package/dist/components/AnalyticsPanel.d.ts +2 -0
  32. package/dist/components/AnalyticsPanel.d.ts.map +1 -0
  33. package/dist/components/AnalyticsPanel.js +151 -0
  34. package/dist/components/AnalyticsPanel.js.map +1 -0
  35. package/dist/components/AuditPanel.d.ts +4 -0
  36. package/dist/components/AuditPanel.d.ts.map +1 -0
  37. package/dist/components/AuditPanel.js +463 -0
  38. package/dist/components/AuditPanel.js.map +1 -0
  39. package/dist/components/AuditsPanel.d.ts +2 -0
  40. package/dist/components/AuditsPanel.d.ts.map +1 -0
  41. package/dist/components/AuditsPanel.js +229 -0
  42. package/dist/components/AuditsPanel.js.map +1 -0
  43. package/dist/components/DigestPanel.d.ts +2 -0
  44. package/dist/components/DigestPanel.d.ts.map +1 -0
  45. package/dist/components/DigestPanel.js +320 -0
  46. package/dist/components/DigestPanel.js.map +1 -0
  47. package/dist/components/FreshnessPanel.d.ts +4 -0
  48. package/dist/components/FreshnessPanel.d.ts.map +1 -0
  49. package/dist/components/FreshnessPanel.js +224 -0
  50. package/dist/components/FreshnessPanel.js.map +1 -0
  51. package/dist/components/InkGradeCell.d.ts +4 -0
  52. package/dist/components/InkGradeCell.d.ts.map +1 -0
  53. package/dist/components/InkGradeCell.js +32 -0
  54. package/dist/components/InkGradeCell.js.map +1 -0
  55. package/dist/components/OnboardingFlow.d.ts +2 -0
  56. package/dist/components/OnboardingFlow.d.ts.map +1 -0
  57. package/dist/components/OnboardingFlow.js +406 -0
  58. package/dist/components/OnboardingFlow.js.map +1 -0
  59. package/dist/components/index.d.ts +8 -0
  60. package/dist/components/index.d.ts.map +1 -0
  61. package/dist/components/index.js +18 -0
  62. package/dist/components/index.js.map +1 -0
  63. package/dist/endpoints/analytics.d.ts +9 -0
  64. package/dist/endpoints/analytics.d.ts.map +1 -0
  65. package/dist/endpoints/analytics.js +68 -0
  66. package/dist/endpoints/analytics.js.map +1 -0
  67. package/dist/endpoints/auditChanges.d.ts +9 -0
  68. package/dist/endpoints/auditChanges.d.ts.map +1 -0
  69. package/dist/endpoints/auditChanges.js +52 -0
  70. package/dist/endpoints/auditChanges.js.map +1 -0
  71. package/dist/endpoints/auditStatus.d.ts +6 -0
  72. package/dist/endpoints/auditStatus.d.ts.map +1 -0
  73. package/dist/endpoints/auditStatus.js +24 -0
  74. package/dist/endpoints/auditStatus.js.map +1 -0
  75. package/dist/endpoints/config.d.ts +8 -0
  76. package/dist/endpoints/config.d.ts.map +1 -0
  77. package/dist/endpoints/config.js +12 -0
  78. package/dist/endpoints/config.js.map +1 -0
  79. package/dist/endpoints/digestPreflight.d.ts +8 -0
  80. package/dist/endpoints/digestPreflight.d.ts.map +1 -0
  81. package/dist/endpoints/digestPreflight.js +79 -0
  82. package/dist/endpoints/digestPreflight.js.map +1 -0
  83. package/dist/endpoints/ga4Test.d.ts +8 -0
  84. package/dist/endpoints/ga4Test.d.ts.map +1 -0
  85. package/dist/endpoints/ga4Test.js +38 -0
  86. package/dist/endpoints/ga4Test.js.map +1 -0
  87. package/dist/endpoints/insights.d.ts +8 -0
  88. package/dist/endpoints/insights.d.ts.map +1 -0
  89. package/dist/endpoints/insights.js +53 -0
  90. package/dist/endpoints/insights.js.map +1 -0
  91. package/dist/endpoints/onboarding.d.ts +4 -0
  92. package/dist/endpoints/onboarding.d.ts.map +1 -0
  93. package/dist/endpoints/onboarding.js +64 -0
  94. package/dist/endpoints/onboarding.js.map +1 -0
  95. package/dist/endpoints/runAudit.d.ts +12 -0
  96. package/dist/endpoints/runAudit.d.ts.map +1 -0
  97. package/dist/endpoints/runAudit.js +174 -0
  98. package/dist/endpoints/runAudit.js.map +1 -0
  99. package/dist/endpoints/runFreshnessAudit.d.ts +13 -0
  100. package/dist/endpoints/runFreshnessAudit.d.ts.map +1 -0
  101. package/dist/endpoints/runFreshnessAudit.js +143 -0
  102. package/dist/endpoints/runFreshnessAudit.js.map +1 -0
  103. package/dist/endpoints/sendDigest.d.ts +10 -0
  104. package/dist/endpoints/sendDigest.d.ts.map +1 -0
  105. package/dist/endpoints/sendDigest.js +259 -0
  106. package/dist/endpoints/sendDigest.js.map +1 -0
  107. package/dist/index.d.ts +4 -0
  108. package/dist/index.d.ts.map +1 -0
  109. package/dist/index.js +6 -0
  110. package/dist/index.js.map +1 -0
  111. package/dist/lib/computeGrade.d.ts +8 -0
  112. package/dist/lib/computeGrade.d.ts.map +1 -0
  113. package/dist/lib/computeGrade.js +48 -0
  114. package/dist/lib/computeGrade.js.map +1 -0
  115. package/dist/plugin.d.ts +28 -0
  116. package/dist/plugin.d.ts.map +1 -0
  117. package/dist/plugin.js +168 -0
  118. package/dist/plugin.js.map +1 -0
  119. package/dist/types.d.ts +99 -0
  120. package/dist/types.d.ts.map +1 -0
  121. package/dist/types.js +3 -0
  122. package/dist/types.js.map +1 -0
  123. package/package.json +75 -0
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeAuditChangesHandler = makeAuditChangesHandler;
4
+ function makeAuditChangesHandler(options) {
5
+ return async (req) => {
6
+ const { payload } = req;
7
+ const last = await payload.find({
8
+ collection: 'ink-audits',
9
+ where: { and: [{ type: { equals: options.type } }, { status: { equals: 'complete' } }] },
10
+ sort: '-completedAt',
11
+ limit: 1,
12
+ depth: 0,
13
+ });
14
+ const lastAudit = last.docs[0];
15
+ const lastRunAt = lastAudit?.completedAt ?? null;
16
+ if (!lastRunAt) {
17
+ return Response.json({
18
+ changedSinceLastRun: true,
19
+ lastRunAt: null,
20
+ changedCount: 0,
21
+ changedSamples: [],
22
+ });
23
+ }
24
+ let totalChanged = 0;
25
+ const changedSamples = [];
26
+ for (const target of options.auditTargets) {
27
+ const result = await payload.find({
28
+ collection: target.collection,
29
+ where: { updatedAt: { greater_than: lastRunAt } },
30
+ limit: 5,
31
+ depth: 0,
32
+ sort: '-updatedAt',
33
+ });
34
+ totalChanged += result.totalDocs;
35
+ for (const doc of result.docs) {
36
+ const r = doc;
37
+ const title = typeof r.title === 'string' && r.title ? r.title : String(r.slug ?? r.id ?? 'untitled');
38
+ const slug = typeof r.slug === 'string' ? r.slug : '';
39
+ const url = slug ? `/${slug}` : `/${target.collection}/${String(r.id)}`;
40
+ const updatedAt = typeof r.updatedAt === 'string' ? r.updatedAt : '';
41
+ changedSamples.push({ title, url, updatedAt });
42
+ }
43
+ }
44
+ return Response.json({
45
+ changedSinceLastRun: totalChanged > 0,
46
+ lastRunAt,
47
+ changedCount: totalChanged,
48
+ changedSamples: changedSamples.slice(0, 5),
49
+ });
50
+ };
51
+ }
52
+ //# sourceMappingURL=auditChanges.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auditChanges.js","sourceRoot":"","sources":["../../src/endpoints/auditChanges.ts"],"names":[],"mappings":";;AAQA,0DAqDC;AArDD,SAAgB,uBAAuB,CAAC,OAAgB;IACtD,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAAA;QAEvB,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YAC9B,UAAU,EAAE,YAAY;YACxB,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE;YACxF,IAAI,EAAE,cAAc;YACpB,KAAK,EAAE,CAAC;YACR,KAAK,EAAE,CAAC;SACT,CAAC,CAAA;QAEF,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAyC,CAAA;QACtE,MAAM,SAAS,GAAG,SAAS,EAAE,WAAW,IAAI,IAAI,CAAA;QAEhD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,QAAQ,CAAC,IAAI,CAAC;gBACnB,mBAAmB,EAAE,IAAI;gBACzB,SAAS,EAAE,IAAI;gBACf,YAAY,EAAE,CAAC;gBACf,cAAc,EAAE,EAAE;aACnB,CAAC,CAAA;QACJ,CAAC;QAED,IAAI,YAAY,GAAG,CAAC,CAAA;QACpB,MAAM,cAAc,GAA6D,EAAE,CAAA;QAEnF,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YAC1C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;gBAChC,UAAU,EAAE,MAAM,CAAC,UAA8D;gBACjF,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE;gBACjD,KAAK,EAAE,CAAC;gBACR,KAAK,EAAE,CAAC;gBACR,IAAI,EAAE,YAAY;aACnB,CAAC,CAAA;YACF,YAAY,IAAI,MAAM,CAAC,SAAS,CAAA;YAChC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC9B,MAAM,CAAC,GAAG,GAA8B,CAAA;gBACxC,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,IAAI,UAAU,CAAC,CAAA;gBACrG,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;gBACrD,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAA;gBACvE,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAA;gBACpE,cAAc,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAA;YAChD,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,CAAC;YACnB,mBAAmB,EAAE,YAAY,GAAG,CAAC;YACrC,SAAS;YACT,YAAY,EAAE,YAAY;YAC1B,cAAc,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;SAC3C,CAAC,CAAA;IACJ,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { PayloadHandler } from 'payload';
2
+ export declare function makeAuditStatusHandler(options: {
3
+ maxAuditsPerDay: number;
4
+ type?: string;
5
+ }): PayloadHandler;
6
+ //# sourceMappingURL=auditStatus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auditStatus.d.ts","sourceRoot":"","sources":["../../src/endpoints/auditStatus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAS,MAAM,SAAS,CAAA;AAEpD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,cAAc,CAmB1G"}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeAuditStatusHandler = makeAuditStatusHandler;
4
+ function makeAuditStatusHandler(options) {
5
+ return async (req) => {
6
+ const { payload } = req;
7
+ const since = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
8
+ const where = options.type
9
+ ? { and: [{ type: { equals: options.type } }, { createdAt: { greater_than: since } }] }
10
+ : { createdAt: { greater_than: since } };
11
+ const { totalDocs } = await payload.find({
12
+ collection: 'ink-audits',
13
+ where,
14
+ limit: 0,
15
+ depth: 0,
16
+ });
17
+ return Response.json({
18
+ usedToday: totalDocs,
19
+ dailyLimit: options.maxAuditsPerDay,
20
+ atLimit: totalDocs >= options.maxAuditsPerDay,
21
+ });
22
+ };
23
+ }
24
+ //# sourceMappingURL=auditStatus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auditStatus.js","sourceRoot":"","sources":["../../src/endpoints/auditStatus.ts"],"names":[],"mappings":";;AAEA,wDAmBC;AAnBD,SAAgB,sBAAsB,CAAC,OAAmD;IACxF,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAAA;QACvB,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAA;QACtE,MAAM,KAAK,GAAU,OAAO,CAAC,IAAI;YAC/B,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE;YACvF,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,CAAA;QAC1C,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YACvC,UAAU,EAAE,YAAY;YACxB,KAAK;YACL,KAAK,EAAE,CAAC;YACR,KAAK,EAAE,CAAC;SACT,CAAC,CAAA;QACF,OAAO,QAAQ,CAAC,IAAI,CAAC;YACnB,SAAS,EAAE,SAAS;YACpB,UAAU,EAAE,OAAO,CAAC,eAAe;YACnC,OAAO,EAAE,SAAS,IAAI,OAAO,CAAC,eAAe;SAC9C,CAAC,CAAA;IACJ,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { PayloadHandler } from 'payload';
2
+ type Options = {
3
+ gaAccessEmail: string;
4
+ superAdminEmails: string[];
5
+ };
6
+ export declare function makeConfigHandler(options: Options): PayloadHandler;
7
+ export {};
8
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/endpoints/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,KAAK,OAAO,GAAG;IACb,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAA;CAC3B,CAAA;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAOlE"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeConfigHandler = makeConfigHandler;
4
+ function makeConfigHandler(options) {
5
+ return async () => {
6
+ return Response.json({
7
+ gaAccessEmail: options.gaAccessEmail,
8
+ superAdminEmails: options.superAdminEmails,
9
+ });
10
+ };
11
+ }
12
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/endpoints/config.ts"],"names":[],"mappings":";;AAOA,8CAOC;AAPD,SAAgB,iBAAiB,CAAC,OAAgB;IAChD,OAAO,KAAK,IAAI,EAAE;QAChB,OAAO,QAAQ,CAAC,IAAI,CAAC;YACnB,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;SAC3C,CAAC,CAAA;IACJ,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { PayloadHandler } from 'payload';
2
+ type Options = {
3
+ serviceUrl: string;
4
+ apiKey: string;
5
+ };
6
+ export declare function makeDigestPreflightHandler(options: Options): PayloadHandler;
7
+ export {};
8
+ //# sourceMappingURL=digestPreflight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"digestPreflight.d.ts","sourceRoot":"","sources":["../../src/endpoints/digestPreflight.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,KAAK,OAAO,GAAG;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAID,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CA6E3E"}
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeDigestPreflightHandler = makeDigestPreflightHandler;
4
+ function makeDigestPreflightHandler(options) {
5
+ return async (req) => {
6
+ const { payload } = req;
7
+ const profileResult = await payload.find({ collection: 'ink-onboarding-profile', limit: 1, depth: 0 });
8
+ const profile = profileResult.docs[0];
9
+ const checks = [];
10
+ if (!profile) {
11
+ checks.push({ ok: false, label: 'Onboarding profile', detail: 'Complete the wizard first.' });
12
+ }
13
+ else {
14
+ checks.push({ ok: true, label: 'Onboarding profile' });
15
+ }
16
+ const recipients = (profile?.digestRecipients ?? [])
17
+ .map((r) => String(r.email ?? ''))
18
+ .filter((e) => /.+@.+\..+/.test(e));
19
+ if (recipients.length === 0) {
20
+ checks.push({
21
+ ok: false,
22
+ label: 'Digest recipients',
23
+ detail: 'Add at least one email in the Onboarding Profile.',
24
+ });
25
+ }
26
+ else {
27
+ checks.push({
28
+ ok: true,
29
+ label: 'Digest recipients',
30
+ detail: `${recipients.length} configured`,
31
+ });
32
+ }
33
+ const propertyId = profile?.ga4PropertyId;
34
+ if (!propertyId) {
35
+ checks.push({
36
+ ok: false,
37
+ label: 'GA4 property ID',
38
+ detail: 'Set ga4PropertyId on the Onboarding Profile.',
39
+ });
40
+ }
41
+ else {
42
+ checks.push({ ok: true, label: 'GA4 property ID', detail: propertyId });
43
+ }
44
+ let resendCheck = { ok: false, label: 'Resend sender domain', detail: 'Could not reach service.' };
45
+ try {
46
+ const res = await fetch(`${options.serviceUrl}/v1/digest/preflight`, {
47
+ headers: { 'x-api-key': options.apiKey },
48
+ });
49
+ const data = await res.json();
50
+ if (data?.resend?.ok) {
51
+ resendCheck = {
52
+ ok: true,
53
+ label: 'Resend sender domain',
54
+ detail: data.resend.status === 'unverified-via-api'
55
+ ? `${data.resend.domain} · send-only key (verification not checkable)`
56
+ : `${data.resend.domain} verified`,
57
+ };
58
+ }
59
+ else {
60
+ resendCheck = {
61
+ ok: false,
62
+ label: 'Resend sender domain',
63
+ detail: data?.resend?.error ?? 'Verification unknown.',
64
+ };
65
+ }
66
+ }
67
+ catch (err) {
68
+ resendCheck = {
69
+ ok: false,
70
+ label: 'Resend sender domain',
71
+ detail: err instanceof Error ? err.message : 'Service unreachable.',
72
+ };
73
+ }
74
+ checks.push(resendCheck);
75
+ const allOk = checks.every((c) => c.ok);
76
+ return Response.json({ ok: allOk, checks });
77
+ };
78
+ }
79
+ //# sourceMappingURL=digestPreflight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"digestPreflight.js","sourceRoot":"","sources":["../../src/endpoints/digestPreflight.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { PayloadHandler } from 'payload';
2
+ type Options = {
3
+ serviceUrl: string;
4
+ apiKey: string;
5
+ };
6
+ export declare function makeGa4TestHandler(options: Options): PayloadHandler;
7
+ export {};
8
+ //# sourceMappingURL=ga4Test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ga4Test.d.ts","sourceRoot":"","sources":["../../src/endpoints/ga4Test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,KAAK,OAAO,GAAG;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CA0CnE"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeGa4TestHandler = makeGa4TestHandler;
4
+ function makeGa4TestHandler(options) {
5
+ return async (req) => {
6
+ const url = new URL(req.url ?? 'http://localhost');
7
+ const propertyId = url.searchParams.get('propertyId')?.trim() ?? '';
8
+ if (!propertyId) {
9
+ return Response.json({ ok: false, error: 'propertyId is required' }, { status: 400 });
10
+ }
11
+ if (!/^\d{6,}$/.test(propertyId)) {
12
+ return Response.json({
13
+ ok: false,
14
+ error: 'GA4 property IDs are numeric (e.g. 337097713). The G-prefixed value is a Measurement ID, not a Property ID.',
15
+ }, { status: 400 });
16
+ }
17
+ try {
18
+ const res = await fetch(`${options.serviceUrl}/v1/analytics/summary`, {
19
+ method: 'POST',
20
+ headers: { 'Content-Type': 'application/json', 'x-api-key': options.apiKey },
21
+ body: JSON.stringify({ propertyId, days: 7 }),
22
+ });
23
+ const data = await res.json();
24
+ if (!res.ok) {
25
+ return Response.json({ ok: false, error: data?.error ?? `Service returned ${res.status}` }, { status: 200 });
26
+ }
27
+ return Response.json({
28
+ ok: true,
29
+ sessions: data?.sessions?.current ?? 0,
30
+ users: data?.users?.current ?? 0,
31
+ });
32
+ }
33
+ catch (err) {
34
+ return Response.json({ ok: false, error: err instanceof Error ? err.message : 'Connection failed' }, { status: 200 });
35
+ }
36
+ };
37
+ }
38
+ //# sourceMappingURL=ga4Test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ga4Test.js","sourceRoot":"","sources":["../../src/endpoints/ga4Test.ts"],"names":[],"mappings":";;AAOA,gDA0CC;AA1CD,SAAgB,kBAAkB,CAAC,OAAgB;IACjD,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,kBAAkB,CAAC,CAAA;QAClD,MAAM,UAAU,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;QACnE,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,wBAAwB,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;QACvF,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YACjC,OAAO,QAAQ,CAAC,IAAI,CAClB;gBACE,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,6GAA6G;aACrH,EACD,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAA;QACH,CAAC;QAED,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,CAAC,UAAU,uBAAuB,EAAE;gBACpE,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,WAAW,EAAE,OAAO,CAAC,MAAM,EAAE;gBAC5E,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;aAC9C,CAAC,CAAA;YACF,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;YAC7B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,OAAO,QAAQ,CAAC,IAAI,CAClB,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,oBAAoB,GAAG,CAAC,MAAM,EAAE,EAAE,EACrE,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAA;YACH,CAAC;YACD,OAAO,QAAQ,CAAC,IAAI,CAAC;gBACnB,EAAE,EAAE,IAAI;gBACR,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,IAAI,CAAC;gBACtC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,IAAI,CAAC;aACjC,CAAC,CAAA;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,QAAQ,CAAC,IAAI,CAClB,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,EAAE,EAC9E,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAA;QACH,CAAC;IACH,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { PayloadHandler } from 'payload';
2
+ type Options = {
3
+ serviceUrl: string;
4
+ apiKey: string;
5
+ };
6
+ export declare function makeInsightsHandler(options: Options): PayloadHandler;
7
+ export {};
8
+ //# sourceMappingURL=insights.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insights.d.ts","sourceRoot":"","sources":["../../src/endpoints/insights.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,KAAK,OAAO,GAAG;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAqDpE"}
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeInsightsHandler = makeInsightsHandler;
4
+ function makeInsightsHandler(options) {
5
+ return async (req) => {
6
+ const { payload } = req;
7
+ const profileResult = await payload.find({ collection: 'ink-onboarding-profile', limit: 1, depth: 0 });
8
+ const profile = profileResult.docs[0];
9
+ const propertyId = profile?.ga4PropertyId;
10
+ if (!propertyId) {
11
+ return Response.json({ error: 'No GA4 property ID configured.' }, { status: 400 });
12
+ }
13
+ const conversionEvents = (profile?.conversionEvents ?? [])
14
+ .map((c) => String(c.eventName ?? c.event ?? ''))
15
+ .filter(Boolean);
16
+ let insights = {};
17
+ try {
18
+ const res = await fetch(`${options.serviceUrl}/v1/analytics/insights`, {
19
+ method: 'POST',
20
+ headers: { 'Content-Type': 'application/json', 'x-api-key': options.apiKey },
21
+ body: JSON.stringify({ propertyId, conversionEvents }),
22
+ });
23
+ const data = await res.json();
24
+ if (!res.ok)
25
+ return Response.json(data, { status: res.status });
26
+ insights = data;
27
+ }
28
+ catch (err) {
29
+ return Response.json({ error: err instanceof Error ? err.message : 'Insights call failed' }, { status: 500 });
30
+ }
31
+ // Also surface the most recent pending audit-derived insights for context.
32
+ const pending = await payload.find({
33
+ collection: 'ink-insights',
34
+ where: { status: { equals: 'new' } },
35
+ sort: '-createdAt',
36
+ limit: 8,
37
+ depth: 0,
38
+ });
39
+ const recentInsights = pending.docs.map((d) => {
40
+ const r = d;
41
+ return {
42
+ id: String(r.id),
43
+ title: String(r.title ?? ''),
44
+ body: String(r.body ?? ''),
45
+ priority: String(r.priority ?? 'medium'),
46
+ pageUrl: typeof r.pageUrl === 'string' ? r.pageUrl : undefined,
47
+ type: String(r.type ?? 'audit-result'),
48
+ };
49
+ });
50
+ return Response.json({ ...insights, recentInsights });
51
+ };
52
+ }
53
+ //# sourceMappingURL=insights.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insights.js","sourceRoot":"","sources":["../../src/endpoints/insights.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { PayloadHandler } from 'payload';
2
+ export declare const onboardingStatusHandler: PayloadHandler;
3
+ export declare const onboardingSubmitHandler: PayloadHandler;
4
+ //# sourceMappingURL=onboarding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../../src/endpoints/onboarding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,eAAO,MAAM,uBAAuB,EAAE,cAYrC,CAAA;AAiBD,eAAO,MAAM,uBAAuB,EAAE,cAkDrC,CAAA"}
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.onboardingSubmitHandler = exports.onboardingStatusHandler = void 0;
4
+ const onboardingStatusHandler = async (req) => {
5
+ const { payload } = req;
6
+ const result = await payload.find({
7
+ collection: 'ink-onboarding-profile',
8
+ limit: 1,
9
+ depth: 0,
10
+ });
11
+ const profile = result.docs[0];
12
+ return Response.json({
13
+ configured: Boolean(profile?.completedAt),
14
+ hasDraft: Boolean(profile && !profile.completedAt),
15
+ });
16
+ };
17
+ exports.onboardingStatusHandler = onboardingStatusHandler;
18
+ const onboardingSubmitHandler = async (req) => {
19
+ const { payload } = req;
20
+ let body;
21
+ try {
22
+ body = (await req.json());
23
+ }
24
+ catch {
25
+ return Response.json({ error: 'Invalid JSON body' }, { status: 400 });
26
+ }
27
+ if (!body.siteName?.trim()) {
28
+ return Response.json({ error: 'Site name is required' }, { status: 400 });
29
+ }
30
+ if (!body.audiences?.length) {
31
+ return Response.json({ error: 'At least one audience segment is required' }, { status: 400 });
32
+ }
33
+ if (!body.objectives?.length) {
34
+ return Response.json({ error: 'At least one objective is required' }, { status: 400 });
35
+ }
36
+ if (!body.brandVoice?.trim()) {
37
+ return Response.json({ error: 'Brand voice is required' }, { status: 400 });
38
+ }
39
+ if (!body.digestRecipients?.length) {
40
+ return Response.json({ error: 'At least one digest recipient is required' }, { status: 400 });
41
+ }
42
+ const data = {
43
+ ...body,
44
+ completedAt: new Date().toISOString(),
45
+ };
46
+ const existing = await payload.find({
47
+ collection: 'ink-onboarding-profile',
48
+ limit: 1,
49
+ depth: 0,
50
+ });
51
+ const saved = existing.docs.length > 0
52
+ ? await payload.update({
53
+ collection: 'ink-onboarding-profile',
54
+ id: existing.docs[0].id,
55
+ data,
56
+ })
57
+ : await payload.create({
58
+ collection: 'ink-onboarding-profile',
59
+ data,
60
+ });
61
+ return Response.json({ profile: saved });
62
+ };
63
+ exports.onboardingSubmitHandler = onboardingSubmitHandler;
64
+ //# sourceMappingURL=onboarding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboarding.js","sourceRoot":"","sources":["../../src/endpoints/onboarding.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { PayloadHandler } from 'payload';
2
+ import type { AuditTarget } from '../types.js';
3
+ type Options = {
4
+ serviceUrl: string;
5
+ apiKey: string;
6
+ auditTargets: AuditTarget[];
7
+ maxAuditsPerDay: number;
8
+ maxAiProposalsPerAudit: number;
9
+ };
10
+ export declare function makeRunAuditHandler(options: Options): PayloadHandler;
11
+ export {};
12
+ //# sourceMappingURL=runAudit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runAudit.d.ts","sourceRoot":"","sources":["../../src/endpoints/runAudit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAG7C,OAAO,KAAK,EAAE,WAAW,EAAkE,MAAM,aAAa,CAAA;AAE9G,KAAK,OAAO,GAAG;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,WAAW,EAAE,CAAA;IAC3B,eAAe,EAAE,MAAM,CAAA;IACvB,sBAAsB,EAAE,MAAM,CAAA;CAC/B,CAAA;AA8CD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAqJpE"}
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeRunAuditHandler = makeRunAuditHandler;
4
+ const seo_js_1 = require("../audits/seo.js");
5
+ const client_js_1 = require("../client.js");
6
+ async function fetchSessionsByPath(serviceUrl, apiKey, propertyId, paths) {
7
+ if (paths.length === 0)
8
+ return {};
9
+ try {
10
+ const res = await fetch(`${serviceUrl}/v1/analytics/page-sessions`, {
11
+ method: 'POST',
12
+ headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },
13
+ body: JSON.stringify({ propertyId, paths, days: 28 }),
14
+ });
15
+ if (!res.ok)
16
+ return {};
17
+ const data = (await res.json());
18
+ return data.sessions ?? {};
19
+ }
20
+ catch {
21
+ return {};
22
+ }
23
+ }
24
+ function computeGrade(records, totalScanned) {
25
+ if (totalScanned === 0)
26
+ return 'A';
27
+ const pct = records.length / totalScanned;
28
+ if (pct === 0)
29
+ return 'A';
30
+ if (pct < 0.1)
31
+ return 'B';
32
+ if (pct < 0.3)
33
+ return 'C';
34
+ if (pct < 0.6)
35
+ return 'D';
36
+ return 'F';
37
+ }
38
+ async function countScanned(payload, targets) {
39
+ let total = 0;
40
+ for (const target of targets) {
41
+ const { totalDocs } = await payload.find({
42
+ collection: target.collection,
43
+ limit: 0,
44
+ depth: 0,
45
+ });
46
+ total += totalDocs;
47
+ }
48
+ return total;
49
+ }
50
+ function makeRunAuditHandler(options) {
51
+ return async (req) => {
52
+ const { payload } = req;
53
+ const since = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
54
+ const { totalDocs: usedToday } = await payload.find({
55
+ collection: 'ink-audits',
56
+ where: { and: [{ type: { equals: 'seo' } }, { createdAt: { greater_than: since } }] },
57
+ limit: 0,
58
+ depth: 0,
59
+ });
60
+ if (usedToday >= options.maxAuditsPerDay) {
61
+ return Response.json({
62
+ error: `Daily audit limit reached (${options.maxAuditsPerDay}/day). Try again in 24 hours.`,
63
+ usedToday,
64
+ dailyLimit: options.maxAuditsPerDay,
65
+ }, { status: 429 });
66
+ }
67
+ const profileResult = await payload.find({
68
+ collection: 'ink-onboarding-profile',
69
+ limit: 1,
70
+ depth: 0,
71
+ });
72
+ if (profileResult.docs.length === 0) {
73
+ return Response.json({ error: 'No onboarding profile found. Complete INK setup first.' }, { status: 400 });
74
+ }
75
+ const profile = profileResult.docs[0];
76
+ const profileSummary = {
77
+ audiences: (profile.audiences ?? []).map((a) => String(a.segment ?? '')).filter(Boolean),
78
+ objectives: (profile.objectives ?? []).map((o) => String(o.objective ?? '')).filter(Boolean),
79
+ conversionEvents: (profile.conversionEvents ?? []).map((e) => String(e.eventName ?? e.event ?? '')).filter(Boolean),
80
+ brandVoice: String(profile.brandVoice ?? ''),
81
+ guardrails: (profile.guardrails ?? []).map((g) => String(g.rule ?? '')).filter(Boolean),
82
+ };
83
+ const audit = await payload.create({
84
+ collection: 'ink-audits',
85
+ data: { type: 'seo', status: 'running' },
86
+ });
87
+ const records = await (0, seo_js_1.runSeoAudit)(payload, options.auditTargets);
88
+ const totalScanned = await countScanned(payload, options.auditTargets);
89
+ const grade = computeGrade(records, totalScanned);
90
+ const propertyId = profile.ga4PropertyId;
91
+ let sessionsByPath = {};
92
+ if (propertyId && records.length > 0) {
93
+ sessionsByPath = await fetchSessionsByPath(options.serviceUrl, options.apiKey, propertyId, records.map((r) => r.url));
94
+ }
95
+ const recordsRanked = [...records]
96
+ .map((r) => ({ record: r, sessions: sessionsByPath[r.url] ?? 0 }))
97
+ .sort((a, b) => b.sessions - a.sessions);
98
+ // Only send pages that need an actual meta rewrite to the AI — OG / heading
99
+ // findings surface in the UI but don't burn tokens.
100
+ const topForAi = recordsRanked
101
+ .filter((x) => x.record.missingTitle || x.record.missingDescription)
102
+ .slice(0, options.maxAiProposalsPerAudit)
103
+ .map((x) => x.record);
104
+ let proposals = [];
105
+ let aiError = null;
106
+ if (topForAi.length > 0) {
107
+ try {
108
+ const result = await (0, client_js_1.callAuditService)(options.serviceUrl, options.apiKey, {
109
+ tenantId: String(profile.id),
110
+ auditType: 'seo',
111
+ pages: topForAi,
112
+ profile: profileSummary,
113
+ });
114
+ proposals = (result.proposals ?? []).map((p) => {
115
+ const record = topForAi.find((r) => r.url === p.url);
116
+ return {
117
+ ...p,
118
+ pageId: record?.pageId,
119
+ collection: record?.collection,
120
+ };
121
+ });
122
+ for (const p of proposals) {
123
+ await payload.create({
124
+ collection: 'ink-insights',
125
+ data: {
126
+ type: 'audit-result',
127
+ title: `Meta suggested for ${p.url}`,
128
+ body: p.rationale,
129
+ priority: 'high',
130
+ pageUrl: p.url,
131
+ status: 'new',
132
+ relatedAudit: audit.id,
133
+ objectiveAlignment: profileSummary.objectives.slice(0, 1).map((objective) => ({ objective })),
134
+ suggestions: [
135
+ ...(p.proposedTitle ? [{ text: `Meta title: ${p.proposedTitle}` }] : []),
136
+ ...(p.proposedDescription ? [{ text: `Meta description: ${p.proposedDescription}` }] : []),
137
+ ],
138
+ proposedMetaTitle: p.proposedTitle,
139
+ proposedMetaDescription: p.proposedDescription,
140
+ },
141
+ });
142
+ }
143
+ }
144
+ catch (err) {
145
+ aiError = err instanceof Error ? err.message : 'AI call failed';
146
+ console.error('[INK] Intelligence Service call failed:', err);
147
+ }
148
+ }
149
+ const recordsWithTraffic = recordsRanked.map((x) => ({ ...x.record, sessions: x.sessions }));
150
+ const summary = records.length === 0
151
+ ? `All ${totalScanned} pages have meta. Nothing to action.`
152
+ : `${records.length} of ${totalScanned} pages need meta. AI proposals generated for top ${proposals.length} by traffic.`;
153
+ const updated = await payload.update({
154
+ collection: 'ink-audits',
155
+ id: audit.id,
156
+ data: {
157
+ status: aiError ? 'failed' : 'complete',
158
+ grade,
159
+ findings: recordsWithTraffic,
160
+ aiResponse: { proposals, summary },
161
+ completedAt: new Date().toISOString(),
162
+ },
163
+ });
164
+ return Response.json({
165
+ audit: updated,
166
+ findingCount: records.length,
167
+ totalScanned,
168
+ proposals,
169
+ summary,
170
+ aiError,
171
+ });
172
+ };
173
+ }
174
+ //# sourceMappingURL=runAudit.js.map