Skip to content

feat(apify): persist TikTok/X post rows like Instagram (chat#1840)#753

Merged
sweetmantech merged 4 commits into
mainfrom
feat/tiktok-x-post-persistence
Jul 3, 2026
Merged

feat(apify): persist TikTok/X post rows like Instagram (chat#1840)#753
sweetmantech merged 4 commits into
mainfrom
feat/tiktok-x-post-persistence

Conversation

@sweetmantech

@sweetmantech sweetmantech commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Item 2 of recoupable/chat#1840. Independent of api#752 (item 1) — no ordering constraint between the two. Contract note: docs#262 (merge docs first). No database dependency.

Why

api#740 made TikTok/X scrapes persist follower counts, but post rows still only persisted for Instagram — handleInstagramProfileScraperResults was the sole caller of upsertPosts/upsertSocialPosts. Succeeded TikTok/X runs left GET /api/artists/{id}/posts empty (issue evidence: runs 9AYX8xyaHWyHtnGtC / bx3asRqfbNnkKgogG SUCCEEDED with real items, zero post rows landed). This is the half of item 2 that #740 didn't cover.

What

  • lib/apify/persistPostsForSocial.ts — the posts half of the Instagram flow (upsertPostsgetPosts → link via upsertSocialPosts), extracted as a shared helper (SRP: one export). No-ops on empty rows; skips linking when the social row isn't found. Called after upsertSocials so first-seen profiles link correctly.
  • TikTok handler: post_urlwebVideoUrl, updated_atcreateTimeISO — fields verified on real run 9AYX8xyaHWyHtnGtC.
  • X handler: post_url ← tweet url (display casing kept — posts key on exact URL), updated_atcreatedAt converted from Twitter's legacy format (Thu Jul 02 17:21:21 +0000 2026) to ISO; invalid dates fall back to the column default. Verified on real run bx3asRqfbNnkKgogG.
  • Instagram handler untouched (already persists; refactoring it onto the helper is a separate cleanup).

Tests (TDD, RED→GREEN)

  • New helper suite: persist+link happy path (exact upsertSocialPosts rows), empty-rows no-op, missing-social skip.
  • Per-platform handler tests extended with the real-run post fields; empty-dataset no-op now also asserts no persistence call.
  • Full lib/apify + lib/supabase suites: 238 passed; tsc --noEmit no new errors vs main; eslint clean.

Done-when (issue) — preview verification to follow as a comment

  • POST /api/socials/{id}/scrape on a TikTok social and an X social → within one webhook cycle, GET /api/artists/{id}/posts returns the scraped posts — verified live 2026-07-03 (49 → 69 posts: +10 tiktok, +10 x), results table in comments

🤖 Generated with Claude Code


Summary by cubic

Persist TikTok and X post rows and link them to socials, matching the Instagram flow, so artist posts now populate for these platforms. Timestamps are normalized to ISO and bad dates no longer break upserts. Implements item 2 of chat#1840.

  • New Features

    • Added persistPostsForSocial to upsert posts and link to the matching social; no-op on empty, skip link if social missing.
    • TikTok: webVideoUrl -> post_url; createTimeISO -> updated_at via toIsoDate (unparseable dates are omitted so the column default applies).
    • X: url -> post_url; legacy createdAt -> ISO updated_at via toIsoDate.
    • GET /api/artists/{id}/posts now returns TikTok/X posts after a scrape.
  • Dependencies

    • Docs contract in docs#262 (merge first). Independent of api#752.

Written for commit 54a21a5. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Saved scraped social posts alongside profile data for TikTok and Twitter.
    • Added support for linking posts to their matching social profile record.
    • Captured post URLs and timestamps during import, including date normalization for older Twitter data.
  • Bug Fixes

    • Improved handling when no posts are available by returning early with empty results.
    • Skips post-to-profile linking when no matching social profile is found.

TikTok and X scrapes persisted follower counts (api#740) but no post
rows — only the Instagram handler wrote posts/social_posts, so
GET /api/artists/{id}/posts stayed empty for those platforms and
succeeded runs left no reusable content data (issue evidence: runs
9AYX8xyaHWyHtnGtC / bx3asRqfbNnkKgogG SUCCEEDED with real items, zero
rows landed).

- lib/apify/persistPostsForSocial.ts — the posts half of the Instagram
  flow (upsertPosts -> getPosts -> link via upsertSocialPosts), shared
  by both handlers; no-ops on empty rows, skips linking when the social
  row is missing.
- TikTok: post_url from webVideoUrl, updated_at from createTimeISO
  (fields verified on real run 9AYX8xyaHWyHtnGtC).
- X: post_url from the tweet url, createdAt converted from Twitter's
  legacy date format to ISO (verified on real run bx3asRqfbNnkKgogG);
  invalid dates fall back to the column default.

TDD RED->GREEN: helper suite (persist+link, empty no-op, missing-social
skip) + per-platform handler assertions with real-run fixtures. Full
lib/apify + lib/supabase suites green (238 tests); tsc no new errors vs
main; eslint clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
api Ready Ready Preview Jul 3, 2026 9:34pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@sweetmantech, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 13 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d88e636b-9238-469d-8121-f2a91e95157f

📥 Commits

Reviewing files that changed from the base of the PR and between 4cb8036 and 54a21a5.

⛔ Files ignored due to path filters (2)
  • lib/apify/__tests__/toIsoDate.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/apify/tiktok/__tests__/handleTiktokProfileScraperResults.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
📒 Files selected for processing (3)
  • lib/apify/tiktok/handleTiktokProfileScraperResults.ts
  • lib/apify/toIsoDate.ts
  • lib/apify/twitter/handleTwitterProfileScraperResults.ts
📝 Walkthrough

Walkthrough

Adds a new persistPostsForSocial helper that upserts post rows, matches them to a social profile, and links them via social_posts. Integrates this helper into TikTok and Twitter profile scraper result handlers, which now extract post URLs/timestamps and return persisted posts alongside social.

Changes

Post persistence feature

Layer / File(s) Summary
persistPostsForSocial core helper
lib/apify/persistPostsForSocial.ts
New function upserts post rows, fetches them by post_url, matches a socials row by normalized profileUrl, and upserts social_posts links when both exist; returns { posts, social }.
TikTok handler integration
lib/apify/tiktok/handleTiktokProfileScraperResults.ts
TiktokPostItem gains webVideoUrl/createTimeISO; builds postRows and calls persistPostsForSocial, returning { social, posts } instead of { social }.
Twitter handler integration
lib/apify/twitter/handleTwitterProfileScraperResults.ts
TweetItem gains url/createdAt; adds toIsoDate conversion helper, builds postRows, calls persistPostsForSocial, and returns { social, posts } instead of { social }.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ScraperHandler as TikTok/Twitter Handler
  participant persistPostsForSocial
  participant PostsTable
  participant SocialsTable
  participant SocialPostsTable

  ScraperHandler->>ScraperHandler: extract post URLs/timestamps
  ScraperHandler->>persistPostsForSocial: postRows, profileUrl
  persistPostsForSocial->>PostsTable: upsert postRows
  persistPostsForSocial->>PostsTable: fetch posts by post_url
  persistPostsForSocial->>SocialsTable: select social by profileUrl
  alt social and posts found
    persistPostsForSocial->>SocialPostsTable: upsert social_posts links
  end
  persistPostsForSocial-->>ScraperHandler: { posts, social }
  ScraperHandler-->>ScraperHandler: return { social, posts }
Loading

Possibly related issues

Possibly related PRs

  • recoupable/api#743: Related to the same scraper handlers, adding a posts depth parameter that affects the volume of posts now persisted/linked via persistPostsForSocial.

Poem

A rabbit hops through posts anew,
Linking tweets and TikToks too,
persistPostsForSocial saves the day,
Upsert, fetch, and link away 🐇
Socials and posts, now hand in hand!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Solid & Clean Code ✅ Passed PASS: the shared posts-persistence flow is extracted cleanly; file/function names match, handlers stay focused, and duplication is reduced with straightforward logic.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tiktok-x-post-persistence

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sweetmantech

Copy link
Copy Markdown
Contributor Author

Preview verification — round 1 (2026-07-03)

Preview https://api-b2lbx276z-recoup.vercel.app confirmed built from 5d41b7c7 (deployment 5303779507 queried by sha, status success).

The end-to-end Done-when (POST /api/socials/{id}/scrape on a TikTok + an X social → webhook → GET /api/artists/{id}/posts returns the posts) needs an account with artist access to the target socials on the preview's environment — and previews turn out not to share the prod key store (a fresh prod-minted key returns 200 on prod /api/accounts/id and 401 here). The session's admin token expired mid-verification.

What was verified against reality today:

  • Real fixture shapes: the handler field mappings were built from the live datasets of the issue's two succeeded runs, fetched via admin poll this session — TikTok 9AYX8xyaHWyHtnGtC (webVideoUrl, createTimeISO) and X bx3asRqfbNnkKgogG (url, createdAt in Twitter's legacy format, converted to ISO). Fixtures in the tests are trimmed from those items.
  • Prod baseline for the e2e diff: GET /api/artists/1058a744-fd58-4cde-b830-88ab4eb97a86/posts today returns 49 posts, all instagram.com URLs, zero TikTok/X — the before-state the e2e check will diff against.
  • Unit suites (helper + both handlers, real-shaped fixtures): 238 passed; tsc/lint clean.

Will post round 2 (live scrape → webhook → posts diff) with a fresh admin token, either against the test deployment after merge or a preview once env access is sorted.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 6 files

Confidence score: 3/5

  • In lib/apify/tiktok/handleTiktokProfileScraperResults.ts, forwarding raw createTimeISO into posts.updated_at can cause otherwise valid TikTok rows to fail persistence when that field is malformed, which risks dropped posts during ingestion — normalize/validate the timestamp (or fall back safely) before writing.
  • In lib/apify/twitter/handleTwitterProfileScraperResults.ts, relying on new Date(value) for Twitter’s legacy date format can parse inconsistently across runtimes, causing tweet times to silently fall back to the DB default and skew post chronology — switch to a deterministic parser for that actor format before merging.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="lib/apify/twitter/handleTwitterProfileScraperResults.ts">

<violation number="1" location="lib/apify/twitter/handleTwitterProfileScraperResults.ts:29">
P2: Tweet timestamps can silently fall back to the posts-table default on runtimes that do not parse Twitter’s legacy date string, because `new Date(value)` relies on implementation-defined parsing for this actor format. A small explicit parser for `EEE MMM dd HH:mm:ss Z yyyy` would keep `updated_at` tied to the tweet time across runtimes.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant Webhook as Apify Webhook
    participant Route as API Route
    participant Handler as TikTok/X Handler
    participant Persist as persistPostsForSocial
    participant DB as Supabase (DB)

    Webhook->>Route: POST /api/socials/{id}/scrape (webhook)
    Route->>Handler: handleTiktok/TwitterProfileScraperResults(payload)

    alt dataset has items with valid data
        Handler->>Handler: Extract post rows from items
        Note over Handler: TikTok: webVideoUrl → post_url, createTimeISO → updated_at
        Note over Handler: X: url → post_url, createdAt → ISO (invalid/absent → column default)
        Handler->>DB: upsertSocials([normalized profile])
        DB-->>Handler: social row

        Handler->>Persist: persistPostsForSocial({postRows, profileUrl})
        Persist->>DB: upsertPosts(postRows)
        DB-->>Persist: 
        Persist->>DB: getPosts(postUrls)
        DB-->>Persist: posts with IDs
        Persist->>DB: selectSocials(profileUrl)
        DB-->>Persist: social row or null

        alt social found AND posts exist
            Persist->>DB: upsertSocialPosts(links)
        else social not found
            Note over Persist: Skip linking – posts stored unlinked
        end

        Persist-->>Handler: {posts, social}
        Handler-->>Route: {social, posts}
    else dataset empty (or no post data)
        Handler->>Handler: No‑op – skip upsertSocials and persistPostsForSocial
        Handler-->>Route: {social: null}
    end

    Route-->>Webhook: 200 OK
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

* (the posts upsert applies its column default). */
const toIsoDate = (value?: string): string | undefined => {
if (!value) return undefined;
const parsed = new Date(value);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Tweet timestamps can silently fall back to the posts-table default on runtimes that do not parse Twitter’s legacy date string, because new Date(value) relies on implementation-defined parsing for this actor format. A small explicit parser for EEE MMM dd HH:mm:ss Z yyyy would keep updated_at tied to the tweet time across runtimes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/apify/twitter/handleTwitterProfileScraperResults.ts, line 29:

<comment>Tweet timestamps can silently fall back to the posts-table default on runtimes that do not parse Twitter’s legacy date string, because `new Date(value)` relies on implementation-defined parsing for this actor format. A small explicit parser for `EEE MMM dd HH:mm:ss Z yyyy` would keep `updated_at` tied to the tweet time across runtimes.</comment>

<file context>
@@ -16,9 +21,19 @@ type TweetItem = {
+ * (the posts upsert applies its column default). */
+const toIsoDate = (value?: string): string | undefined => {
+  if (!value) return undefined;
+  const parsed = new Date(value);
+  return isNaN(parsed.getTime()) ? undefined : parsed.toISOString();
+};
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Triaged in 54a21a5: kept new Date() rather than a hand-rolled EEE MMM dd HH:mm:ss Z yyyy parser — this service deploys only to Node/V8 (Vercel functions), where parsing of this format is deterministic, and the extracted toIsoDate now has a unit test pinning the exact Twitter string to its ISO result, so any runtime change fails loudly in CI instead of silently. A custom parser adds surface without a reachable failure mode here (KISS/YAGNI); the graceful fallback (unparseable → column default) already bounds the worst case.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parent comment is too broad for this PR: this service only runs on Node/V8, the legacy Twitter format is pinned in unit tests, and unparseable dates already fall back to the column default. In that setup, a hand-rolled parser adds surface without a reachable failure mode here.

Thanks for the feedback! I've saved this as a new learning to improve future reviews.

Comment thread lib/apify/tiktok/handleTiktokProfileScraperResults.ts Outdated
@sweetmantech

Copy link
Copy Markdown
Contributor Author

Preview verification — round 2 (2026-07-03) ✅ end-to-end

Preview https://api-b2lbx276z-recoup.vercel.app (head 5d41b7c7), tested with a preview-Privy admin Bearer token. Note: previews share the production Supabase (confirmed via list_projects — one project), so this is a live diff on real rows, same method as api#740's verification.

Baseline (before): GET /api/artists/1058a744…/posts49 posts, 100% instagram.com, zero TikTok/X (the issue's downstream symptom).

Live run: POST /api/socials/{id}/scrape?posts=10 on the artist's TikTok and X socials → runs jQzDU3uR2JbEpUVGF (TikTok) and RRV75MiciJbCYhhwX (X), both SUCCEEDED with 10 items; webhooks delivered to this preview (deployment-local getBaseUrl), new handlers ran.

check expected observed verdict
TikTok posts rows 10 rows, webVideoUrl + ISO createTimeISO 10 rows (tiktok.com/@brauxelion/video/…)
X posts rows 10 rows, tweet url + legacy createdAt → ISO 10 rows (x.com/Brauxelion_/status/…, e.g. 2026-07-03T18:18:22+00:00)
social_posts links keyed to handler-normalized social rows 10 → 2df656a3 (tiktok.com/@brauxelion), 10 → 91a0ff74 (x.com/brauxelion_)
socials freshness counts + updated_at fresh TikTok 382 followers, X 963, both updated 20:25Z this run
Done-when: GET /api/artists/{id}/posts returns the scraped posts TikTok + X visible 69 total = 49 instagram + 10 x + 10 tiktok

One data finding along the way (not a code issue in this PR): the artist was linked to the legacy tiktok.com/brauxelion row (doubly-linked, stale, 0 followers) while the handler correctly persists to the canonical tiktok.com/@brauxelion row the actor reports — so TikTok posts persisted but didn't surface until the link was corrected. Fixed at the data layer (linked 2df656a3, removed both stale legacy links), same action as the issue's earlier Done item — and another data point for the known no link/unlink-social API endpoint gap tracked on chat#1840.

Cost note: 2 × (5+10) = 30 credits on the testing account, one Apify actor run per platform.

@sweetmantech sweetmantech changed the base branch from test to main July 3, 2026 20:36
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
lib/apify/twitter/handleTwitterProfileScraperResults.ts (1)

24-31: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

toIsoDate relies on implementation-defined Date parsing.

new Date(value) on Twitter's legacy format ("Thu Jul 02 17:21:21 +0000 2026") is not part of the format the ECMAScript spec guarantees — "the key rule: only string formats defined in the ECMAScript specification are guaranteed to work across all environments", everything else is implementation-specific. V8/Node happens to parse this format today (and the author verified against real data), but this isn't a spec guarantee across runtimes/versions.

The isNaN guard is good defensive coding regardless. If you want to remove the implementation-specific dependency, an explicit parser (e.g. date-fns/parse with the known Twitter format string) would be more robust — though given this is already verified against production data, this is more a documented risk than a required fix.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/apify/twitter/handleTwitterProfileScraperResults.ts` around lines 24 -
31, The `toIsoDate` helper currently depends on `new Date(value)` parsing
Twitter’s legacy timestamp format, which is implementation-specific. Update
`toIsoDate` to use an explicit parser for the known format (for example in the
`handleTwitterProfileScraperResults` flow) instead of relying on built-in `Date`
parsing, while keeping the existing invalid/absent date handling and `isNaN`
guard. Use the `toIsoDate` symbol to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@lib/apify/twitter/handleTwitterProfileScraperResults.ts`:
- Around line 24-31: The `toIsoDate` helper currently depends on `new
Date(value)` parsing Twitter’s legacy timestamp format, which is
implementation-specific. Update `toIsoDate` to use an explicit parser for the
known format (for example in the `handleTwitterProfileScraperResults` flow)
instead of relying on built-in `Date` parsing, while keeping the existing
invalid/absent date handling and `isNaN` guard. Use the `toIsoDate` symbol to
locate the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 51462068-aa90-459a-b8cb-9c879e0577c9

📥 Commits

Reviewing files that changed from the base of the PR and between 2f59abc and 4cb8036.

⛔ Files ignored due to path filters (3)
  • lib/apify/__tests__/persistPostsForSocial.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/apify/tiktok/__tests__/handleTiktokProfileScraperResults.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/apify/twitter/__tests__/handleTwitterProfileScraperResults.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
📒 Files selected for processing (3)
  • lib/apify/persistPostsForSocial.ts
  • lib/apify/tiktok/handleTiktokProfileScraperResults.ts
  • lib/apify/twitter/handleTwitterProfileScraperResults.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 1 file (changes from recent commits).

Requires human review: Auto-approval blocked by 2 unresolved issues from previous reviews.

Re-trigger cubic

/** The actor emits Twitter's legacy format ("Thu Jul 02 17:21:21 +0000 2026");
* Postgres can't parse it, so convert to ISO. Invalid/absent dates → undefined
* (the posts upsert applies its column default). */
const toIsoDate = (value?: string): string | undefined => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SRP - new lib file for toIsoDate

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 54a21a5toIsoDate extracted to lib/apify/toIsoDate.ts with its own unit suite (Twitter legacy format, ISO passthrough, unparseable → undefined, empty/absent → undefined). Both handlers now import it.

Review feedback (api#753):
- SRP: toIsoDate moves from an inline helper in the Twitter handler to
  lib/apify/toIsoDate.ts (one exported function per file), with its own
  unit suite pinning the Twitter legacy format on this Node/V8 runtime.
- cubic P2 (valid): the TikTok mapper forwarded raw createTimeISO into
  posts.updated_at, so one malformed date could fail the whole upsert
  batch. Both handlers now normalize through toIsoDate; unparseable
  dates fall back to the column default.

lib/apify suite: 86 passed; prettier + eslint + tsc clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 5 files (changes from recent commits).

Requires human review: Adds new business logic for persisting posts from TikTok/X scrapers with database writes and date parsing. Not a trivial change; requires human review for data integrity and edge cases.

Re-trigger cubic

@sweetmantech sweetmantech merged commit 177c041 into main Jul 3, 2026
6 checks passed
@sweetmantech sweetmantech deleted the feat/tiktok-x-post-persistence branch July 3, 2026 21:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant