dh-remixer-sdk 0.0.25 → 0.0.26-1a0cd0b

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dh-remixer-sdk",
3
- "version": "0.0.25",
3
+ "version": "0.0.26-1a0cd0b",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "templates",
@@ -289,6 +289,9 @@ export function buildRedirectHelmetHtml(
289
289
  );
290
290
  }
291
291
 
292
+ const bodyTag = pick(/(<body\b[^>]*>)/i, prerenderedHtml) || "<body>";
293
+ const bodyInner = pick(/<body\b[^>]*>([\s\S]*?)<\/body>/i, prerenderedHtml);
294
+
292
295
  // --- redirect script ------------------------------------------------------
293
296
  out.push(
294
297
  `<script type="text/javascript">
@@ -320,6 +323,8 @@ export function buildRedirectHelmetHtml(
320
323
  <head>
321
324
  ${out.filter(Boolean).join("\n")}
322
325
  </head>
323
- <body></body>
326
+ ${bodyTag}
327
+ ${bodyInner || ""}
328
+ </body>
324
329
  </html>`;
325
330
  }
@@ -53,7 +53,7 @@ async function main() {
53
53
  }
54
54
  prerenderer = null;
55
55
  }
56
-
56
+
57
57
  if (attempt === maxRetries) {
58
58
  throw new Error(`Failed to create Prerenderer after ${maxRetries} attempts: ${err.message}`);
59
59
  }
@@ -100,6 +100,17 @@ async function main() {
100
100
  );
101
101
  console.log("[created] sitemap.xml");
102
102
 
103
+ await fs.writeFile(
104
+ path.join(REMIXER_BUILD_DIR, "robots.txt"),
105
+ `User-agent: *
106
+ Allow: /
107
+
108
+ Sitemap: https://${TARGET_DOMAIN}/sitemap.xml
109
+ `,
110
+ "utf8",
111
+ );
112
+ console.log("[created] robots.txt");
113
+
103
114
  await prerenderer.close();
104
115
  }
105
116
 
@@ -26,21 +26,73 @@ export class Prerenderer {
26
26
 
27
27
  async #renderPage(route) {
28
28
  const r = this.#normalize(route);
29
- const { Page, Runtime } = this.#client;
29
+ const { Page, Runtime, Network } = this.#client;
30
+
31
+ await Network.enable();
32
+
33
+ let inflight = 0;
34
+
35
+ // Track network activity
36
+ Network.requestWillBeSent(() => {
37
+ inflight++;
38
+ });
39
+
40
+ Network.loadingFinished(() => {
41
+ inflight = Math.max(0, inflight - 1);
42
+ });
43
+
44
+ Network.loadingFailed(() => {
45
+ inflight = Math.max(0, inflight - 1);
46
+ });
30
47
 
31
48
  // Navigate
32
49
  await Page.navigate({ url: `${this.#origin}${r}` });
33
50
  await Page.loadEventFired();
34
51
 
35
- // Wait for Helmet (non-fatal)
52
+ // Wait for network idle (no requests for 500ms)
53
+ await new Promise((resolve) => {
54
+ let idleTimer;
55
+
56
+ const checkIdle = () => {
57
+ if (inflight === 0) {
58
+ idleTimer = globalThis.setTimeout(() => resolve(), 500);
59
+ } else {
60
+ clearTimeout(idleTimer);
61
+ globalThis.setTimeout(checkIdle, 100);
62
+ }
63
+ };
64
+
65
+ checkIdle();
66
+ });
67
+
68
+ // Wait for real DOM content (NOT just Helmet)
36
69
  await Runtime.evaluate({
37
70
  expression: `
38
71
  (async function () {
39
72
  const deadline = Date.now() + 15000;
73
+
74
+ function hasContent() {
75
+ const root = document.querySelector('#root') || document.body;
76
+
77
+ if (!root) return false;
78
+
79
+ // Must have meaningful text
80
+ const text = root.innerText || '';
81
+ if (text.trim().length < 100) return false;
82
+
83
+ // Must have some actual elements
84
+ const meaningful = root.querySelector(
85
+ 'h1, h2, h3, p, img, ul, ol, li, section, article'
86
+ );
87
+
88
+ return !!meaningful;
89
+ }
90
+
40
91
  while (Date.now() < deadline) {
41
- if (document.head && document.head.querySelector('[data-react-helmet="true"]')) return true;
92
+ if (hasContent()) return true;
42
93
  await new Promise(r => setTimeout(r, 50));
43
94
  }
95
+
44
96
  return false;
45
97
  })()
46
98
  `,
@@ -48,13 +100,14 @@ export class Prerenderer {
48
100
  returnByValue: true,
49
101
  });
50
102
 
103
+ // Small buffer to let layout settle
51
104
  await Runtime.evaluate({
52
105
  expression: `new Promise(r => setTimeout(r, 100))`,
53
106
  awaitPromise: true,
54
107
  returnByValue: true,
55
108
  });
56
109
 
57
- // Snapshot
110
+ // Extract FULL HTML (including body)
58
111
  const { result } = await Runtime.evaluate({
59
112
  expression: "document.documentElement.outerHTML",
60
113
  returnByValue: true,
@@ -7,5 +7,6 @@
7
7
  "tsconfig.json": "tsconfig.json",
8
8
  "vite.config.ts": "vite.config.ts",
9
9
  "robots.txt": "public/robots.txt",
10
- ".htaccess": "public/.htaccess"
10
+ ".htaccess": "public/.htaccess",
11
+ "supabase.ts": "lib/supabase.ts"
11
12
  }
@@ -0,0 +1,73 @@
1
+
2
+ import { createClient } from '@supabase/supabase-js';
3
+
4
+ /**
5
+ * 1) supabase — user-scoped client
6
+ * • Before login: uses the project access token
7
+ * • After login: automatically swaps to the user's JWT
8
+ */
9
+ export const supabase = createClient(
10
+ import.meta.env.VITE_SUPABASE_URL!,
11
+ import.meta.env.VITE_SUPABASE_ANON_KEY!,
12
+ );
13
+
14
+ /**
15
+ * 2) supabaseProject — tenant-wide client
16
+ * • Always uses the project access token
17
+ * • For public or calendar-style views where you want every project's data
18
+ */
19
+ export const supabaseProject = createClient(
20
+ import.meta.env.VITE_SUPABASE_URL!,
21
+ import.meta.env.VITE_SUPABASE_ANON_KEY!,
22
+ {
23
+ global: {
24
+ headers: {
25
+ Authorization: `Bearer ${import.meta.env.VITE_PROJECT_ACCESS_TOKEN}`,
26
+ },
27
+ },
28
+ auth: {
29
+ // disable session persistence so it never picks up a user JWT
30
+ detectSessionInUrl: false,
31
+ persistSession: false,
32
+ autoRefreshToken: false,
33
+ },
34
+ }
35
+ );
36
+
37
+ /**
38
+ * authHeader() — helper for manual fetch() calls (Edge Functions/REST)
39
+ * • Ensures project_id is set in user_metadata and session is refreshed
40
+ * • Returns `{ Authorization: 'Bearer <token>' }` where `<token>` is:
41
+ * - the logged-in user's JWT (if session exists), or
42
+ * - the project access token (anonymous)
43
+ */
44
+ export async function authHeader() {
45
+ let {
46
+ data: { session },
47
+ } = await supabase.auth.getSession();
48
+
49
+ if (session?.user) {
50
+ const currentProjectId = import.meta.env.VITE_PROJECT_ID;
51
+ const userProjectId = session.user.user_metadata?.project_id;
52
+
53
+ if (userProjectId !== currentProjectId) {
54
+ const { error } = await supabase.auth.updateUser({
55
+ data: {
56
+ ...session.user.user_metadata,
57
+ project_id: currentProjectId,
58
+ },
59
+ });
60
+ if (error) throw error;
61
+
62
+ // Force session refresh to get new JWT with updated project_id
63
+ const { data: refreshData, error: refreshError } = await supabase.auth.refreshSession();
64
+ if (refreshError) throw refreshError;
65
+ session = refreshData.session;
66
+ }
67
+ }
68
+
69
+ const token =
70
+ session?.access_token ?? import.meta.env.VITE_PROJECT_ACCESS_TOKEN;
71
+
72
+ return { Authorization: `Bearer ${token}` };
73
+ }
@@ -49,7 +49,7 @@ export const createCheckoutSession = async (
49
49
  payload.guest_email = params.guestEmail;
50
50
  }
51
51
 
52
- if (params.shippingRateId) {
52
+ if (params.shippingRateId && params.mode !== 'subscription') {
53
53
  payload.shipping_rate_id = params.shippingRateId;
54
54
  payload.shipping_cost = params.shippingCost;
55
55
  if (params.shippingLabel) {