dh-remixer-sdk 0.0.4 → 0.0.6

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.4",
3
+ "version": "0.0.6",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "templates",
@@ -8,6 +8,10 @@ function isObj(x) {
8
8
  }
9
9
 
10
10
  function jsonMerge(low, high) {
11
+ if (Array.isArray(low) && Array.isArray(high)) {
12
+ return [...low, ...high];
13
+ }
14
+
11
15
  if (!isObj(low) || !isObj(high)) {
12
16
  return high === undefined ? low : high;
13
17
  }
@@ -18,9 +22,9 @@ function jsonMerge(low, high) {
18
22
  const hv = high[key];
19
23
 
20
24
  if (hv === undefined) return [key, lv];
25
+ if (Array.isArray(lv) && Array.isArray(hv)) return [key, [...lv, ...hv]];
21
26
  if (Array.isArray(lv) || Array.isArray(hv)) return [key, hv];
22
27
  if (isObj(lv) && isObj(hv)) return [key, jsonMerge(lv, hv)];
23
-
24
28
  return [key, hv];
25
29
  }),
26
30
  );
@@ -105,10 +109,26 @@ async function main() {
105
109
  const sdkVersion = JSON.parse(await fs.readFile(sdkPkgPath, "utf8")).version;
106
110
  const templateRoot = path.join(sdkRoot, "templates", templateType);
107
111
  const baseRoot = path.join(sdkRoot, "templates", "base");
108
- const cleanupPath = path.join(baseRoot, "cleanup.json");
109
- const filemapPath = path.join(baseRoot, "filemap.json");
110
- const cleanup = JSON.parse(await fs.readFile(cleanupPath, "utf8"));
111
- const filemap = JSON.parse(await fs.readFile(filemapPath, "utf8"));
112
+ const baseCleanupPath = path.join(baseRoot, "cleanup.json");
113
+ const baseFilemapPath = path.join(baseRoot, "filemap.json");
114
+ const templateCleanupPath = path.join(templateRoot, "cleanup.json");
115
+ const templateFilemapPath = path.join(templateRoot, "filemap.json");
116
+ let cleanup = JSON.parse(await fs.readFile(baseCleanupPath, "utf8"));
117
+ let filemap = JSON.parse(await fs.readFile(baseFilemapPath, "utf8"));
118
+
119
+ if (fssync.existsSync(templateCleanupPath)) {
120
+ cleanup = jsonMerge(
121
+ cleanup,
122
+ JSON.parse(await fs.readFile(templateCleanupPath, "utf8")),
123
+ );
124
+ }
125
+
126
+ if (fssync.existsSync(templateFilemapPath)) {
127
+ filemap = jsonMerge(
128
+ cleanup,
129
+ JSON.parse(await fs.readFile(templateFilemapPath, "utf8")),
130
+ );
131
+ }
112
132
 
113
133
  // Delete old files
114
134
  await Promise.all(
@@ -1,11 +1,11 @@
1
1
  {
2
- "index.html": "index.html",
3
2
  "index.tsx": "index.tsx",
3
+ "gitignore": ".gitignore",
4
+ "index.html": "index.html",
4
5
  "package.json": "package.json",
5
6
  "filemap.json": "filemap.json",
6
- ".htaccess": "public/.htaccess",
7
- "robots.txt": "public/robots.txt",
8
- ".gitignore": ".gitignore",
9
7
  "tsconfig.json": "tsconfig.json",
10
- "vite.config.ts": "vite.config.ts"
8
+ "vite.config.ts": "vite.config.ts",
9
+ "robots.txt": "public/robots.txt",
10
+ ".htaccess": "public/.htaccess"
11
11
  }
@@ -0,0 +1,4 @@
1
+ node_modules
2
+ dist
3
+ out
4
+ tsconfig.tsbuildinfo
@@ -0,0 +1,4 @@
1
+ [
2
+ "lib/stripe-checkout.ts",
3
+ "lib/supabase.ts"
4
+ ]
@@ -0,0 +1,4 @@
1
+ {
2
+ "stripe-checkout.ts": "lib/stripe-checkout.ts",
3
+ "supabase.ts": "lib/supabase.ts"
4
+ }
@@ -0,0 +1,55 @@
1
+ import { authHeader, supabase } from "@/lib/supabase";
2
+
3
+ export interface CreateCheckoutSessionParams {
4
+ items: { priceId: string; quantity: number }[];
5
+ mode: "payment" | "subscription";
6
+ successUrl: string;
7
+ cancelUrl: string;
8
+ guestEmail?: string;
9
+ }
10
+
11
+ export type CheckoutSessionResponse = {
12
+ url: string;
13
+ };
14
+
15
+ export const createCheckoutSession = async (
16
+ params: CreateCheckoutSessionParams
17
+ ): Promise<CheckoutSessionResponse> => {
18
+ const headers = await authHeader();
19
+
20
+ const {
21
+ data: { user },
22
+ } = await supabase.auth.getUser();
23
+ const isAuthenticated = !!user;
24
+ const apiUrl = `${import.meta.env.VITE_SUPABASE_URL}/functions/v1/stripe-checkout`;
25
+
26
+ const payload: Record<string, unknown> = {
27
+ line_items: params.items.map((item) => ({
28
+ price_id: item.priceId,
29
+ quantity: item.quantity,
30
+ })),
31
+ mode: params.mode,
32
+ success_url: params.successUrl,
33
+ cancel_url: params.cancelUrl,
34
+ };
35
+
36
+ if (!isAuthenticated && params.guestEmail) {
37
+ (payload as any).guest_email = params.guestEmail;
38
+ }
39
+
40
+ const response = await fetch(apiUrl, {
41
+ method: "POST",
42
+ headers: {
43
+ ...headers,
44
+ "Content-Type": "application/json",
45
+ },
46
+ body: JSON.stringify(payload),
47
+ });
48
+
49
+ if (!response.ok) {
50
+ const error = await response.json();
51
+ throw new Error(error.error || "Failed to create checkout session");
52
+ }
53
+
54
+ return response.json();
55
+ };
@@ -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
+ }