@whop/react-native 0.0.2

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/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # @whop/react-native
2
+
@@ -0,0 +1,11 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ export {
9
+ __require
10
+ };
11
+ //# sourceMappingURL=chunk-BJTO5JO5.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ presets: ["module:@react-native/babel-preset"],
3
+ plugins: ["@babel/plugin-transform-export-namespace-from"],
4
+ };
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,360 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // src/cli/index.ts
26
+ var import_node_fs2 = require("fs");
27
+ var import_node_path2 = __toESM(require("path"));
28
+ var import_node_util = require("util");
29
+ var import_find_up2 = require("find-up");
30
+ var import_rimraf = require("rimraf");
31
+
32
+ // src/cli/mobile.ts
33
+ var import_node_fs = require("fs");
34
+ var import_promises = require("fs/promises");
35
+ var import_node_path = __toESM(require("path"));
36
+ var import_metro_config = require("@react-native/metro-config");
37
+ var import_find_up = require("find-up");
38
+ var import_jszip = __toESM(require("jszip"));
39
+ var import_metro = require("metro");
40
+
41
+ // src/cli/file.ts
42
+ var import_node_crypto = require("crypto");
43
+
44
+ // src/cli/sdk.ts
45
+ var import_api = require("@whop/api");
46
+ var import_dotenv = require("dotenv");
47
+ (0, import_dotenv.config)({
48
+ path: [".env", ".env.local", ".env.development", ".env.production"]
49
+ });
50
+ function env(key) {
51
+ const value = process.env[key];
52
+ if (!value) {
53
+ throw new Error(`Missing environment variable: ${key}`);
54
+ }
55
+ return value;
56
+ }
57
+ var AGENT_USER_ID = env("NEXT_PUBLIC_WHOP_AGENT_USER_ID");
58
+ var COMPANY_ID = env("NEXT_PUBLIC_WHOP_COMPANY_ID");
59
+ var APP_ID = env("NEXT_PUBLIC_WHOP_APP_ID");
60
+ var whopSdk = (0, import_api.WhopServerSdk)({
61
+ appApiKey: env("WHOP_API_KEY"),
62
+ appId: APP_ID,
63
+ companyId: COMPANY_ID,
64
+ onBehalfOfUserId: AGENT_USER_ID
65
+ });
66
+
67
+ // src/cli/file.ts
68
+ async function uploadFile(data, name, contentType) {
69
+ const file = new File([data], name, {
70
+ type: contentType
71
+ });
72
+ const uploadedFile = await whopSdk.withUser(AGENT_USER_ID).attachments.uploadAttachment({
73
+ file,
74
+ record: "app",
75
+ id: APP_ID
76
+ });
77
+ return uploadedFile;
78
+ }
79
+ async function getChecksum(data) {
80
+ const hash = (0, import_node_crypto.createHash)("sha256");
81
+ hash.update(data);
82
+ return hash.digest("hex");
83
+ }
84
+
85
+ // src/cli/valid-view-type.ts
86
+ var VALID_VIEW_TYPES = ["experience-view", "discover-view"];
87
+
88
+ // src/cli/mobile.ts
89
+ async function buildAndPublish(root, platform, {
90
+ shouldBuild = true,
91
+ shouldUpload = true
92
+ } = {
93
+ shouldBuild: true,
94
+ shouldUpload: true
95
+ }) {
96
+ if (shouldBuild) {
97
+ await bundle(root, platform);
98
+ }
99
+ if (shouldUpload) {
100
+ await createMobileBuild(root, platform);
101
+ }
102
+ }
103
+ async function bundle(root, platform) {
104
+ await makeEntrypoint(root, platform);
105
+ const outputFile = import_node_path.default.join(
106
+ root,
107
+ "build",
108
+ "output",
109
+ platform,
110
+ "main_js_bundle"
111
+ );
112
+ await (0, import_promises.mkdir)(import_node_path.default.dirname(outputFile), { recursive: true });
113
+ const defaultConfig = (0, import_metro_config.getDefaultConfig)(root);
114
+ const babelLocation = require.resolve("@babel/runtime/package");
115
+ const bableNodeModules = await (0, import_find_up.findUp)("node_modules", {
116
+ cwd: babelLocation,
117
+ type: "directory"
118
+ });
119
+ if (!bableNodeModules) {
120
+ throw new Error("babel node_modules parent folder not found");
121
+ }
122
+ await (0, import_metro.runBuild)(
123
+ {
124
+ ...defaultConfig,
125
+ projectRoot: root,
126
+ transformer: {
127
+ ...defaultConfig.transformer,
128
+ babelTransformerPath: require.resolve("./whop-react-native-babel-transformer.js")
129
+ },
130
+ watchFolders: [
131
+ root,
132
+ import_node_path.default.resolve(root, "node_modules"),
133
+ bableNodeModules
134
+ ],
135
+ reporter: new CustomReporter(),
136
+ resolver: {
137
+ ...defaultConfig.resolver,
138
+ nodeModulesPaths: [
139
+ ...defaultConfig.resolver?.nodeModulesPaths ?? [],
140
+ bableNodeModules
141
+ ]
142
+ }
143
+ },
144
+ {
145
+ dev: false,
146
+ entry: `build/entrypoints/${platform}/index.js`,
147
+ minify: false,
148
+ platform,
149
+ sourceMap: false,
150
+ out: outputFile
151
+ }
152
+ );
153
+ await (0, import_promises.rename)(
154
+ `${outputFile}.js`,
155
+ import_node_path.default.join(root, "build", "output", platform, "main_js_bundle.hbc")
156
+ );
157
+ console.log(` \u2714\uFE0E [${platform}] bundle created`);
158
+ }
159
+ async function getSupportedAppViewTypes(root) {
160
+ const views = await (0, import_promises.readdir)(import_node_path.default.join(root, "src", "views"), {
161
+ withFileTypes: true,
162
+ recursive: false
163
+ });
164
+ const files = views.filter((file) => file.isFile()).map((file) => file.name.split(".")[0]).filter((file) => !!file);
165
+ const validViews = files.filter(
166
+ (file) => VALID_VIEW_TYPES.includes(file)
167
+ );
168
+ if (validViews.length === 0) {
169
+ throw new Error(
170
+ `No valid views found, please create a view in the src/views folder and name it with a valid view type: ${VALID_VIEW_TYPES.join(", ")}`
171
+ );
172
+ }
173
+ return validViews;
174
+ }
175
+ async function makeEntrypoint(root, platform) {
176
+ const entrypoint = import_node_path.default.join(
177
+ root,
178
+ "build",
179
+ "entrypoints",
180
+ platform,
181
+ "index.js"
182
+ );
183
+ const files = await getSupportedAppViewTypes(root);
184
+ const pascalCase = (str) => str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
185
+ const imports = files.map(
186
+ (file) => `import { ${pascalCase(file)} } from "../../../src/views/${file}";`
187
+ );
188
+ const registry = files.map(
189
+ (file) => `AppRegistry.registerComponent("${pascalCase(file)}", () => ${pascalCase(file)});`
190
+ );
191
+ const entrypointContent = `import { AppRegistry } from "react-native";
192
+ ${imports.join("\n")}
193
+
194
+ ${registry.join("\n")}
195
+ `;
196
+ const entrypointDir = import_node_path.default.dirname(entrypoint);
197
+ await (0, import_promises.mkdir)(entrypointDir, { recursive: true });
198
+ await (0, import_promises.writeFile)(entrypoint, entrypointContent, "utf-8");
199
+ console.log(` \u2714\uFE0E [${platform}] entrypoint created`);
200
+ return entrypoint;
201
+ }
202
+ async function createMobileBuild(root, platform) {
203
+ const viewTypes = await getSupportedAppViewTypes(root);
204
+ const fullDirectory = import_node_path.default.join(root, "build", "output", platform);
205
+ const mainJsBundle = import_node_path.default.join(fullDirectory, "main_js_bundle.hbc");
206
+ if (!(0, import_node_fs.existsSync)(mainJsBundle)) {
207
+ throw new Error(`main_js_bundle.hbc not found in ${fullDirectory}`);
208
+ }
209
+ const files = (0, import_node_fs.readdirSync)(fullDirectory);
210
+ if (files.length > 2 || files.length < 1 || !files.includes("main_js_bundle.hbc")) {
211
+ throw new Error(
212
+ "Directory must contain only the main_js_bundle.hbc file and an optional `assets` folder"
213
+ );
214
+ }
215
+ if (files.length === 2 && !files.includes("assets")) {
216
+ throw new Error(
217
+ "Directory must contain only the main_js_bundle.hbc file and an optional `assets` folder"
218
+ );
219
+ }
220
+ const zipData = await zipDirectory(fullDirectory);
221
+ const checksum = await getChecksum(zipData);
222
+ console.log(` \u2714\uFE0E [${platform}] build zipped with checksum: ${checksum}`);
223
+ const fileName = `app_build_${platform}.zip`;
224
+ const uploadedFile = await uploadFile(zipData, fileName, "application/zip");
225
+ console.log(
226
+ ` \u2714\uFE0E [${platform}] uploaded build: ${fileName} (${(zipData.length / 1024).toFixed(0)} KB)`
227
+ );
228
+ const build = await whopSdk.apps.createAppBuild({
229
+ attachment: { directUploadId: uploadedFile.directUploadId },
230
+ checksum,
231
+ platform,
232
+ supportedAppViewTypes: viewTypes.map(
233
+ (view) => ({
234
+ "experience-view": "hub",
235
+ "discover-view": "discover"
236
+ })[view]
237
+ )
238
+ });
239
+ if (!build) {
240
+ throw new Error("Failed to create app build");
241
+ }
242
+ const dashboardUrl = `https://whop.com/dashboard/${COMPANY_ID}/developer/apps/${APP_ID}/builds/`;
243
+ console.log(`
244
+ \u2714\uFE0E [${platform}] deployed as development build \u2714\uFE0E
245
+ - build id: ${build.id}
246
+ - view types: ${viewTypes.join(", ")}
247
+ - promote to production here: ${dashboardUrl}
248
+ `);
249
+ return build;
250
+ }
251
+ async function zipDirectory(directory) {
252
+ const zip = new import_jszip.default();
253
+ function addFilesToZip(currentPath, relativePath = "") {
254
+ const items = (0, import_node_fs.readdirSync)(currentPath);
255
+ for (const item of items) {
256
+ const fullPath = import_node_path.default.join(currentPath, item);
257
+ const zipPath = relativePath ? import_node_path.default.join(relativePath, item) : item;
258
+ const stats = (0, import_node_fs.statSync)(fullPath);
259
+ if (stats.isDirectory()) {
260
+ addFilesToZip(fullPath, zipPath);
261
+ } else {
262
+ const fileContent = (0, import_node_fs.readFileSync)(fullPath);
263
+ zip.file(zipPath, fileContent);
264
+ }
265
+ }
266
+ }
267
+ addFilesToZip(directory);
268
+ const zipData = await zip.generateAsync({ type: "nodebuffer" });
269
+ return zipData;
270
+ }
271
+ var CustomReporter = class {
272
+ update(event) {
273
+ }
274
+ };
275
+
276
+ // src/cli/index.ts
277
+ async function main() {
278
+ const args = (0, import_node_util.parseArgs)({
279
+ options: {
280
+ ios: {
281
+ type: "boolean"
282
+ },
283
+ android: {
284
+ type: "boolean"
285
+ },
286
+ web: {
287
+ type: "boolean"
288
+ }
289
+ },
290
+ strict: true,
291
+ allowPositionals: true,
292
+ args: process.argv.slice(2)
293
+ });
294
+ const [command] = args.positionals;
295
+ let shouldBuild = true;
296
+ let shouldUpload = true;
297
+ let shouldClean = true;
298
+ if (command === "build") {
299
+ shouldUpload = false;
300
+ } else if (command === "ship") {
301
+ shouldBuild = true;
302
+ shouldUpload = true;
303
+ } else if (command === "upload") {
304
+ shouldBuild = false;
305
+ shouldClean = false;
306
+ } else if (command === "clean") {
307
+ shouldBuild = false;
308
+ shouldUpload = false;
309
+ } else {
310
+ console.error(
311
+ `Usage:
312
+ whop-react-native ship [--ios] [--android] [--web] # runs build and then publishes it as a dev build to whop.
313
+ whop-react-native build [--ios] [--android] [--web] # builds your app into a distributable bundle in the build/ directory.
314
+ whop-react-native upload [--ios] [--android] [--web] # uploads the existing build directory to whop.
315
+ whop-react-native clean # cleans the build directory.`
316
+ );
317
+ process.exit(1);
318
+ }
319
+ const root = await getRootProjectDirectory();
320
+ if (shouldClean) {
321
+ await cleanBuildDirectory(root);
322
+ }
323
+ const didProvidePlatform = args.values.ios || args.values.android || args.values.web;
324
+ const opts = { shouldBuild, shouldUpload };
325
+ const promises = [];
326
+ if (args.values.ios || !didProvidePlatform) {
327
+ promises.push(buildAndPublish(root, "ios", opts));
328
+ }
329
+ if (args.values.android || !didProvidePlatform) {
330
+ promises.push(buildAndPublish(root, "android", opts));
331
+ }
332
+ if (args.values.web || !didProvidePlatform) {
333
+ console.warn(" - [web] builds for web are not supported yet - coming soon");
334
+ }
335
+ await Promise.all(promises);
336
+ }
337
+ async function cleanBuildDirectory(root) {
338
+ const buildDirectory = import_node_path2.default.join(root, "build");
339
+ if ((0, import_node_fs2.existsSync)(buildDirectory)) {
340
+ await (0, import_rimraf.rimraf)(buildDirectory);
341
+ }
342
+ console.log(" \u2714\uFE0E cleaned build directory");
343
+ }
344
+ async function getRootProjectDirectory() {
345
+ const file = await (0, import_find_up2.findUp)("package.json", { cwd: process.cwd() });
346
+ if (!file) {
347
+ throw new Error(
348
+ "please run this command inside a whop react native project"
349
+ );
350
+ }
351
+ const root = import_node_path2.default.dirname(file);
352
+ return root;
353
+ }
354
+ main().catch((err) => {
355
+ console.error(err);
356
+ process.exit(1);
357
+ }).then(() => {
358
+ process.exit(0);
359
+ });
360
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/cli/mobile.ts","../../src/cli/file.ts","../../src/cli/sdk.ts","../../src/cli/valid-view-type.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport { findUp } from \"find-up\";\nimport { rimraf } from \"rimraf\";\nimport { buildAndPublish } from \"./mobile\";\n\nasync function main() {\n\tconst args = parseArgs({\n\t\toptions: {\n\t\t\tios: {\n\t\t\t\ttype: \"boolean\",\n\t\t\t},\n\t\t\tandroid: {\n\t\t\t\ttype: \"boolean\",\n\t\t\t},\n\t\t\tweb: {\n\t\t\t\ttype: \"boolean\",\n\t\t\t},\n\t\t},\n\t\tstrict: true,\n\t\tallowPositionals: true,\n\t\targs: process.argv.slice(2),\n\t});\n\n\tconst [command] = args.positionals;\n\n\tlet shouldBuild = true;\n\tlet shouldUpload = true;\n\tlet shouldClean = true;\n\tif (command === \"build\") {\n\t\tshouldUpload = false;\n\t} else if (command === \"ship\") {\n\t\tshouldBuild = true;\n\t\tshouldUpload = true;\n\t} else if (command === \"upload\") {\n\t\tshouldBuild = false;\n\t\tshouldClean = false;\n\t} else if (command === \"clean\") {\n\t\tshouldBuild = false;\n\t\tshouldUpload = false;\n\t} else {\n\t\tconsole.error(\n\t\t\t`Usage:\n\twhop-react-native ship [--ios] [--android] [--web] # runs build and then publishes it as a dev build to whop.\n\twhop-react-native build [--ios] [--android] [--web] # builds your app into a distributable bundle in the build/ directory.\n\twhop-react-native upload [--ios] [--android] [--web] # uploads the existing build directory to whop.\n\twhop-react-native clean # cleans the build directory.`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\tconst root = await getRootProjectDirectory();\n\n\tif (shouldClean) {\n\t\tawait cleanBuildDirectory(root);\n\t}\n\n\tconst didProvidePlatform =\n\t\targs.values.ios || args.values.android || args.values.web;\n\n\tconst opts = { shouldBuild, shouldUpload };\n\tconst promises: Promise<void>[] = [];\n\n\tif (args.values.ios || !didProvidePlatform) {\n\t\tpromises.push(buildAndPublish(root, \"ios\", opts));\n\t}\n\tif (args.values.android || !didProvidePlatform) {\n\t\tpromises.push(buildAndPublish(root, \"android\", opts));\n\t}\n\tif (args.values.web || !didProvidePlatform) {\n\t\tconsole.warn(\" - [web] builds for web are not supported yet - coming soon\");\n\t}\n\n\tawait Promise.all(promises);\n}\n\nasync function cleanBuildDirectory(root: string) {\n\tconst buildDirectory = path.join(root, \"build\");\n\tif (existsSync(buildDirectory)) {\n\t\tawait rimraf(buildDirectory);\n\t}\n\tconsole.log(\" ✔︎ cleaned build directory\");\n}\n\nasync function getRootProjectDirectory() {\n\tconst file = await findUp(\"package.json\", { cwd: process.cwd() });\n\tif (!file) {\n\t\tthrow new Error(\n\t\t\t\"please run this command inside a whop react native project\",\n\t\t);\n\t}\n\tconst root = path.dirname(file);\n\treturn root;\n}\n\nmain()\n\t.catch((err) => {\n\t\tconsole.error(err);\n\t\tprocess.exit(1);\n\t})\n\t.then(() => {\n\t\tprocess.exit(0);\n\t});\n","import { existsSync, readFileSync, readdirSync, statSync } from \"node:fs\";\nimport { mkdir, readdir, rename, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { getDefaultConfig } from \"@react-native/metro-config\";\nimport { findUp } from \"find-up\";\nimport JSZip from \"jszip\";\nimport { type ReportableEvent, type Reporter, runBuild } from \"metro\";\nimport { getChecksum, uploadFile } from \"./file\";\nimport { APP_ID, COMPANY_ID, whopSdk } from \"./sdk\";\nimport { VALID_VIEW_TYPES } from \"./valid-view-type\";\n\nexport async function buildAndPublish(\n\troot: string,\n\tplatform: \"ios\" | \"android\",\n\t{\n\t\tshouldBuild = true,\n\t\tshouldUpload = true,\n\t}: { shouldBuild: boolean; shouldUpload: boolean } = {\n\t\tshouldBuild: true,\n\t\tshouldUpload: true,\n\t},\n) {\n\tif (shouldBuild) {\n\t\tawait bundle(root, platform);\n\t}\n\tif (shouldUpload) {\n\t\tawait createMobileBuild(root, platform);\n\t}\n}\n\nexport async function bundle(root: string, platform: \"ios\" | \"android\") {\n\tawait makeEntrypoint(root, platform);\n\n\tconst outputFile = path.join(\n\t\troot,\n\t\t\"build\",\n\t\t\"output\",\n\t\tplatform,\n\t\t\"main_js_bundle\",\n\t);\n\tawait mkdir(path.dirname(outputFile), { recursive: true });\n\n\tconst defaultConfig = getDefaultConfig(root);\n\n\tconst babelLocation = require.resolve(\"@babel/runtime/package\");\n\n\tconst bableNodeModules = await findUp(\"node_modules\", {\n\t\tcwd: babelLocation,\n\t\ttype: \"directory\",\n\t});\n\tif (!bableNodeModules) {\n\t\tthrow new Error(\"babel node_modules parent folder not found\");\n\t}\n\n\tawait runBuild(\n\t\t{\n\t\t\t...defaultConfig,\n\t\t\tprojectRoot: root,\n\t\t\ttransformer: {\n\t\t\t\t...defaultConfig.transformer,\n\t\t\t\tbabelTransformerPath: require.resolve(\n\t\t\t\t\t\"./whop-react-native-babel-transformer.js\",\n\t\t\t\t),\n\t\t\t},\n\t\t\twatchFolders: [\n\t\t\t\troot,\n\t\t\t\tpath.resolve(root, \"node_modules\"),\n\t\t\t\tbableNodeModules,\n\t\t\t],\n\t\t\treporter: new CustomReporter(),\n\t\t\tresolver: {\n\t\t\t\t...defaultConfig.resolver,\n\t\t\t\tnodeModulesPaths: [\n\t\t\t\t\t...(defaultConfig.resolver?.nodeModulesPaths ?? []),\n\t\t\t\t\tbableNodeModules,\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdev: false,\n\t\t\tentry: `build/entrypoints/${platform}/index.js`,\n\t\t\tminify: false,\n\t\t\tplatform: platform,\n\t\t\tsourceMap: false,\n\t\t\tout: outputFile,\n\t\t},\n\t);\n\n\tawait rename(\n\t\t`${outputFile}.js`,\n\t\tpath.join(root, \"build\", \"output\", platform, \"main_js_bundle.hbc\"),\n\t);\n\n\tconsole.log(` ✔︎ [${platform}] bundle created`);\n}\n\nasync function getSupportedAppViewTypes(\n\troot: string,\n): Promise<(typeof VALID_VIEW_TYPES)[number][]> {\n\tconst views = await readdir(path.join(root, \"src\", \"views\"), {\n\t\twithFileTypes: true,\n\t\trecursive: false,\n\t});\n\tconst files = views\n\t\t.filter((file) => file.isFile())\n\t\t.map((file) => file.name.split(\".\")[0])\n\t\t.filter((file) => !!file);\n\n\tconst validViews = files.filter((file) =>\n\t\tVALID_VIEW_TYPES.includes(file as (typeof VALID_VIEW_TYPES)[number]),\n\t) as (typeof VALID_VIEW_TYPES)[number][];\n\n\tif (validViews.length === 0) {\n\t\tthrow new Error(\n\t\t\t`No valid views found, please create a view in the src/views folder and name it with a valid view type: ${VALID_VIEW_TYPES.join(\", \")}`,\n\t\t);\n\t}\n\n\treturn validViews;\n}\n\nasync function makeEntrypoint(\n\troot: string,\n\tplatform: \"ios\" | \"android\",\n): Promise<string> {\n\tconst entrypoint = path.join(\n\t\troot,\n\t\t\"build\",\n\t\t\"entrypoints\",\n\t\tplatform,\n\t\t\"index.js\",\n\t);\n\n\tconst files = await getSupportedAppViewTypes(root);\n\n\tconst pascalCase = (str: string) =>\n\t\tstr\n\t\t\t.split(\"-\")\n\t\t\t.map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n\t\t\t.join(\"\");\n\n\tconst imports = files.map(\n\t\t(file) =>\n\t\t\t`import { ${pascalCase(file)} } from \"../../../src/views/${file}\";`,\n\t);\n\tconst registry = files.map(\n\t\t(file) =>\n\t\t\t`AppRegistry.registerComponent(\"${pascalCase(file)}\", () => ${pascalCase(file)});`,\n\t);\n\n\tconst entrypointContent = `import { AppRegistry } from \"react-native\";\n${imports.join(\"\\n\")}\n\n${registry.join(\"\\n\")}\n`;\n\n\tconst entrypointDir = path.dirname(entrypoint);\n\tawait mkdir(entrypointDir, { recursive: true });\n\tawait writeFile(entrypoint, entrypointContent, \"utf-8\");\n\n\tconsole.log(` ✔︎ [${platform}] entrypoint created`);\n\n\treturn entrypoint;\n}\n\nexport async function createMobileBuild(\n\troot: string,\n\tplatform: \"ios\" | \"android\",\n) {\n\tconst viewTypes = await getSupportedAppViewTypes(root);\n\n\tconst fullDirectory = path.join(root, \"build\", \"output\", platform);\n\n\t// Check if the directory contains a file called `main_js_bundle.hbc`\n\tconst mainJsBundle = path.join(fullDirectory, \"main_js_bundle.hbc\");\n\n\tif (!existsSync(mainJsBundle)) {\n\t\tthrow new Error(`main_js_bundle.hbc not found in ${fullDirectory}`);\n\t}\n\n\t// check that that folder only contains the main_js_bundle.hbc file and an optional `assets` folder\n\tconst files = readdirSync(fullDirectory);\n\tif (\n\t\tfiles.length > 2 ||\n\t\tfiles.length < 1 ||\n\t\t!files.includes(\"main_js_bundle.hbc\")\n\t) {\n\t\tthrow new Error(\n\t\t\t\"Directory must contain only the main_js_bundle.hbc file and an optional `assets` folder\",\n\t\t);\n\t}\n\tif (files.length === 2 && !files.includes(\"assets\")) {\n\t\tthrow new Error(\n\t\t\t\"Directory must contain only the main_js_bundle.hbc file and an optional `assets` folder\",\n\t\t);\n\t}\n\n\t// Zip the directory\n\tconst zipData = await zipDirectory(fullDirectory);\n\n\tconst checksum = await getChecksum(zipData);\n\n\tconsole.log(` ✔︎ [${platform}] build zipped with checksum: ${checksum}`);\n\n\tconst fileName = `app_build_${platform}.zip`;\n\tconst uploadedFile = await uploadFile(zipData, fileName, \"application/zip\");\n\n\tconsole.log(\n\t\t` ✔︎ [${platform}] uploaded build: ${fileName} (${(zipData.length / 1024).toFixed(0)} KB)`,\n\t);\n\n\tconst build = await whopSdk.apps.createAppBuild({\n\t\tattachment: { directUploadId: uploadedFile.directUploadId },\n\t\tchecksum,\n\t\tplatform,\n\t\tsupportedAppViewTypes: viewTypes.map(\n\t\t\t(view) =>\n\t\t\t\t({\n\t\t\t\t\t\"experience-view\": \"hub\" as const,\n\t\t\t\t\t\"discover-view\": \"discover\" as const,\n\t\t\t\t})[view],\n\t\t),\n\t});\n\n\tif (!build) {\n\t\tthrow new Error(\"Failed to create app build\");\n\t}\n\n\tconst dashboardUrl = `https://whop.com/dashboard/${COMPANY_ID}/developer/apps/${APP_ID}/builds/`;\n\n\tconsole.log(`\\n ✔︎ [${platform}] deployed as development build ✔︎\n - build id: ${build.id}\n - view types: ${viewTypes.join(\", \")}\n - promote to production here: ${dashboardUrl}\\n`);\n\n\treturn build;\n}\n\nasync function zipDirectory(\n\tdirectory: string,\n): Promise<Buffer<ArrayBufferLike>> {\n\tconst zip = new JSZip();\n\n\t// Recursively add files to zip\n\tfunction addFilesToZip(currentPath: string, relativePath = \"\") {\n\t\tconst items = readdirSync(currentPath);\n\n\t\tfor (const item of items) {\n\t\t\tconst fullPath = path.join(currentPath, item);\n\t\t\tconst zipPath = relativePath ? path.join(relativePath, item) : item;\n\t\t\tconst stats = statSync(fullPath);\n\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\taddFilesToZip(fullPath, zipPath);\n\t\t\t} else {\n\t\t\t\tconst fileContent = readFileSync(fullPath);\n\t\t\t\tzip.file(zipPath, fileContent);\n\t\t\t}\n\t\t}\n\t}\n\n\taddFilesToZip(directory);\n\n\t// Generate zip file\n\tconst zipData = await zip.generateAsync({ type: \"nodebuffer\" });\n\n\treturn zipData;\n}\n\nclass CustomReporter implements Reporter {\n\tupdate(event: ReportableEvent) {\n\t\t// Do nothing.\n\t}\n}\n","import { createHash } from \"node:crypto\";\nimport { AGENT_USER_ID, APP_ID, whopSdk } from \"./sdk\";\n\nexport async function uploadFile(\n\tdata: Buffer<ArrayBufferLike>,\n\tname: string,\n\tcontentType: string,\n) {\n\tconst file = new File([data], name, {\n\t\ttype: contentType,\n\t});\n\n\tconst uploadedFile = await whopSdk\n\t\t.withUser(AGENT_USER_ID)\n\t\t.attachments.uploadAttachment({\n\t\t\tfile,\n\t\t\trecord: \"app\",\n\t\t\tid: APP_ID,\n\t\t});\n\n\treturn uploadedFile;\n}\n\nexport async function getChecksum(data: Buffer<ArrayBufferLike>) {\n\tconst hash = createHash(\"sha256\");\n\thash.update(data);\n\treturn hash.digest(\"hex\");\n}\n","import { WhopServerSdk } from \"@whop/api\";\nimport { config } from \"dotenv\";\n\nconfig({\n\tpath: [\".env\", \".env.local\", \".env.development\", \".env.production\"],\n});\n\nfunction env(key: string) {\n\tconst value = process.env[key];\n\tif (!value) {\n\t\tthrow new Error(`Missing environment variable: ${key}`);\n\t}\n\treturn value;\n}\n\nexport const AGENT_USER_ID = env(\"NEXT_PUBLIC_WHOP_AGENT_USER_ID\");\nexport const COMPANY_ID = env(\"NEXT_PUBLIC_WHOP_COMPANY_ID\");\nexport const APP_ID = env(\"NEXT_PUBLIC_WHOP_APP_ID\");\n\nexport const whopSdk: WhopServerSdk = WhopServerSdk({\n\tappApiKey: env(\"WHOP_API_KEY\"),\n\tappId: APP_ID,\n\tcompanyId: COMPANY_ID,\n\tonBehalfOfUserId: AGENT_USER_ID,\n});\n","export const VALID_VIEW_TYPES = [\"experience-view\", \"discover-view\"] as const;\n"],"mappings":"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","names":["import_node_fs","import_node_path","import_find_up","path","JSZip","path"]}
@@ -0,0 +1,342 @@
1
+ import {
2
+ __require
3
+ } from "../chunk-BJTO5JO5.mjs";
4
+
5
+ // src/cli/index.ts
6
+ import { existsSync as existsSync2 } from "fs";
7
+ import path2 from "path";
8
+ import { parseArgs } from "util";
9
+ import { findUp as findUp2 } from "find-up";
10
+ import { rimraf } from "rimraf";
11
+
12
+ // src/cli/mobile.ts
13
+ import { existsSync, readFileSync, readdirSync, statSync } from "fs";
14
+ import { mkdir, readdir, rename, writeFile } from "fs/promises";
15
+ import path from "path";
16
+ import { getDefaultConfig } from "@react-native/metro-config";
17
+ import { findUp } from "find-up";
18
+ import JSZip from "jszip";
19
+ import { runBuild } from "metro";
20
+
21
+ // src/cli/file.ts
22
+ import { createHash } from "crypto";
23
+
24
+ // src/cli/sdk.ts
25
+ import { WhopServerSdk } from "@whop/api";
26
+ import { config } from "dotenv";
27
+ config({
28
+ path: [".env", ".env.local", ".env.development", ".env.production"]
29
+ });
30
+ function env(key) {
31
+ const value = process.env[key];
32
+ if (!value) {
33
+ throw new Error(`Missing environment variable: ${key}`);
34
+ }
35
+ return value;
36
+ }
37
+ var AGENT_USER_ID = env("NEXT_PUBLIC_WHOP_AGENT_USER_ID");
38
+ var COMPANY_ID = env("NEXT_PUBLIC_WHOP_COMPANY_ID");
39
+ var APP_ID = env("NEXT_PUBLIC_WHOP_APP_ID");
40
+ var whopSdk = WhopServerSdk({
41
+ appApiKey: env("WHOP_API_KEY"),
42
+ appId: APP_ID,
43
+ companyId: COMPANY_ID,
44
+ onBehalfOfUserId: AGENT_USER_ID
45
+ });
46
+
47
+ // src/cli/file.ts
48
+ async function uploadFile(data, name, contentType) {
49
+ const file = new File([data], name, {
50
+ type: contentType
51
+ });
52
+ const uploadedFile = await whopSdk.withUser(AGENT_USER_ID).attachments.uploadAttachment({
53
+ file,
54
+ record: "app",
55
+ id: APP_ID
56
+ });
57
+ return uploadedFile;
58
+ }
59
+ async function getChecksum(data) {
60
+ const hash = createHash("sha256");
61
+ hash.update(data);
62
+ return hash.digest("hex");
63
+ }
64
+
65
+ // src/cli/valid-view-type.ts
66
+ var VALID_VIEW_TYPES = ["experience-view", "discover-view"];
67
+
68
+ // src/cli/mobile.ts
69
+ async function buildAndPublish(root, platform, {
70
+ shouldBuild = true,
71
+ shouldUpload = true
72
+ } = {
73
+ shouldBuild: true,
74
+ shouldUpload: true
75
+ }) {
76
+ if (shouldBuild) {
77
+ await bundle(root, platform);
78
+ }
79
+ if (shouldUpload) {
80
+ await createMobileBuild(root, platform);
81
+ }
82
+ }
83
+ async function bundle(root, platform) {
84
+ await makeEntrypoint(root, platform);
85
+ const outputFile = path.join(
86
+ root,
87
+ "build",
88
+ "output",
89
+ platform,
90
+ "main_js_bundle"
91
+ );
92
+ await mkdir(path.dirname(outputFile), { recursive: true });
93
+ const defaultConfig = getDefaultConfig(root);
94
+ const babelLocation = __require.resolve("@babel/runtime/package");
95
+ const bableNodeModules = await findUp("node_modules", {
96
+ cwd: babelLocation,
97
+ type: "directory"
98
+ });
99
+ if (!bableNodeModules) {
100
+ throw new Error("babel node_modules parent folder not found");
101
+ }
102
+ await runBuild(
103
+ {
104
+ ...defaultConfig,
105
+ projectRoot: root,
106
+ transformer: {
107
+ ...defaultConfig.transformer,
108
+ babelTransformerPath: __require.resolve(
109
+ "./whop-react-native-babel-transformer.js"
110
+ )
111
+ },
112
+ watchFolders: [
113
+ root,
114
+ path.resolve(root, "node_modules"),
115
+ bableNodeModules
116
+ ],
117
+ reporter: new CustomReporter(),
118
+ resolver: {
119
+ ...defaultConfig.resolver,
120
+ nodeModulesPaths: [
121
+ ...defaultConfig.resolver?.nodeModulesPaths ?? [],
122
+ bableNodeModules
123
+ ]
124
+ }
125
+ },
126
+ {
127
+ dev: false,
128
+ entry: `build/entrypoints/${platform}/index.js`,
129
+ minify: false,
130
+ platform,
131
+ sourceMap: false,
132
+ out: outputFile
133
+ }
134
+ );
135
+ await rename(
136
+ `${outputFile}.js`,
137
+ path.join(root, "build", "output", platform, "main_js_bundle.hbc")
138
+ );
139
+ console.log(` \u2714\uFE0E [${platform}] bundle created`);
140
+ }
141
+ async function getSupportedAppViewTypes(root) {
142
+ const views = await readdir(path.join(root, "src", "views"), {
143
+ withFileTypes: true,
144
+ recursive: false
145
+ });
146
+ const files = views.filter((file) => file.isFile()).map((file) => file.name.split(".")[0]).filter((file) => !!file);
147
+ const validViews = files.filter(
148
+ (file) => VALID_VIEW_TYPES.includes(file)
149
+ );
150
+ if (validViews.length === 0) {
151
+ throw new Error(
152
+ `No valid views found, please create a view in the src/views folder and name it with a valid view type: ${VALID_VIEW_TYPES.join(", ")}`
153
+ );
154
+ }
155
+ return validViews;
156
+ }
157
+ async function makeEntrypoint(root, platform) {
158
+ const entrypoint = path.join(
159
+ root,
160
+ "build",
161
+ "entrypoints",
162
+ platform,
163
+ "index.js"
164
+ );
165
+ const files = await getSupportedAppViewTypes(root);
166
+ const pascalCase = (str) => str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
167
+ const imports = files.map(
168
+ (file) => `import { ${pascalCase(file)} } from "../../../src/views/${file}";`
169
+ );
170
+ const registry = files.map(
171
+ (file) => `AppRegistry.registerComponent("${pascalCase(file)}", () => ${pascalCase(file)});`
172
+ );
173
+ const entrypointContent = `import { AppRegistry } from "react-native";
174
+ ${imports.join("\n")}
175
+
176
+ ${registry.join("\n")}
177
+ `;
178
+ const entrypointDir = path.dirname(entrypoint);
179
+ await mkdir(entrypointDir, { recursive: true });
180
+ await writeFile(entrypoint, entrypointContent, "utf-8");
181
+ console.log(` \u2714\uFE0E [${platform}] entrypoint created`);
182
+ return entrypoint;
183
+ }
184
+ async function createMobileBuild(root, platform) {
185
+ const viewTypes = await getSupportedAppViewTypes(root);
186
+ const fullDirectory = path.join(root, "build", "output", platform);
187
+ const mainJsBundle = path.join(fullDirectory, "main_js_bundle.hbc");
188
+ if (!existsSync(mainJsBundle)) {
189
+ throw new Error(`main_js_bundle.hbc not found in ${fullDirectory}`);
190
+ }
191
+ const files = readdirSync(fullDirectory);
192
+ if (files.length > 2 || files.length < 1 || !files.includes("main_js_bundle.hbc")) {
193
+ throw new Error(
194
+ "Directory must contain only the main_js_bundle.hbc file and an optional `assets` folder"
195
+ );
196
+ }
197
+ if (files.length === 2 && !files.includes("assets")) {
198
+ throw new Error(
199
+ "Directory must contain only the main_js_bundle.hbc file and an optional `assets` folder"
200
+ );
201
+ }
202
+ const zipData = await zipDirectory(fullDirectory);
203
+ const checksum = await getChecksum(zipData);
204
+ console.log(` \u2714\uFE0E [${platform}] build zipped with checksum: ${checksum}`);
205
+ const fileName = `app_build_${platform}.zip`;
206
+ const uploadedFile = await uploadFile(zipData, fileName, "application/zip");
207
+ console.log(
208
+ ` \u2714\uFE0E [${platform}] uploaded build: ${fileName} (${(zipData.length / 1024).toFixed(0)} KB)`
209
+ );
210
+ const build = await whopSdk.apps.createAppBuild({
211
+ attachment: { directUploadId: uploadedFile.directUploadId },
212
+ checksum,
213
+ platform,
214
+ supportedAppViewTypes: viewTypes.map(
215
+ (view) => ({
216
+ "experience-view": "hub",
217
+ "discover-view": "discover"
218
+ })[view]
219
+ )
220
+ });
221
+ if (!build) {
222
+ throw new Error("Failed to create app build");
223
+ }
224
+ const dashboardUrl = `https://whop.com/dashboard/${COMPANY_ID}/developer/apps/${APP_ID}/builds/`;
225
+ console.log(`
226
+ \u2714\uFE0E [${platform}] deployed as development build \u2714\uFE0E
227
+ - build id: ${build.id}
228
+ - view types: ${viewTypes.join(", ")}
229
+ - promote to production here: ${dashboardUrl}
230
+ `);
231
+ return build;
232
+ }
233
+ async function zipDirectory(directory) {
234
+ const zip = new JSZip();
235
+ function addFilesToZip(currentPath, relativePath = "") {
236
+ const items = readdirSync(currentPath);
237
+ for (const item of items) {
238
+ const fullPath = path.join(currentPath, item);
239
+ const zipPath = relativePath ? path.join(relativePath, item) : item;
240
+ const stats = statSync(fullPath);
241
+ if (stats.isDirectory()) {
242
+ addFilesToZip(fullPath, zipPath);
243
+ } else {
244
+ const fileContent = readFileSync(fullPath);
245
+ zip.file(zipPath, fileContent);
246
+ }
247
+ }
248
+ }
249
+ addFilesToZip(directory);
250
+ const zipData = await zip.generateAsync({ type: "nodebuffer" });
251
+ return zipData;
252
+ }
253
+ var CustomReporter = class {
254
+ update(event) {
255
+ }
256
+ };
257
+
258
+ // src/cli/index.ts
259
+ async function main() {
260
+ const args = parseArgs({
261
+ options: {
262
+ ios: {
263
+ type: "boolean"
264
+ },
265
+ android: {
266
+ type: "boolean"
267
+ },
268
+ web: {
269
+ type: "boolean"
270
+ }
271
+ },
272
+ strict: true,
273
+ allowPositionals: true,
274
+ args: process.argv.slice(2)
275
+ });
276
+ const [command] = args.positionals;
277
+ let shouldBuild = true;
278
+ let shouldUpload = true;
279
+ let shouldClean = true;
280
+ if (command === "build") {
281
+ shouldUpload = false;
282
+ } else if (command === "ship") {
283
+ shouldBuild = true;
284
+ shouldUpload = true;
285
+ } else if (command === "upload") {
286
+ shouldBuild = false;
287
+ shouldClean = false;
288
+ } else if (command === "clean") {
289
+ shouldBuild = false;
290
+ shouldUpload = false;
291
+ } else {
292
+ console.error(
293
+ `Usage:
294
+ whop-react-native ship [--ios] [--android] [--web] # runs build and then publishes it as a dev build to whop.
295
+ whop-react-native build [--ios] [--android] [--web] # builds your app into a distributable bundle in the build/ directory.
296
+ whop-react-native upload [--ios] [--android] [--web] # uploads the existing build directory to whop.
297
+ whop-react-native clean # cleans the build directory.`
298
+ );
299
+ process.exit(1);
300
+ }
301
+ const root = await getRootProjectDirectory();
302
+ if (shouldClean) {
303
+ await cleanBuildDirectory(root);
304
+ }
305
+ const didProvidePlatform = args.values.ios || args.values.android || args.values.web;
306
+ const opts = { shouldBuild, shouldUpload };
307
+ const promises = [];
308
+ if (args.values.ios || !didProvidePlatform) {
309
+ promises.push(buildAndPublish(root, "ios", opts));
310
+ }
311
+ if (args.values.android || !didProvidePlatform) {
312
+ promises.push(buildAndPublish(root, "android", opts));
313
+ }
314
+ if (args.values.web || !didProvidePlatform) {
315
+ console.warn(" - [web] builds for web are not supported yet - coming soon");
316
+ }
317
+ await Promise.all(promises);
318
+ }
319
+ async function cleanBuildDirectory(root) {
320
+ const buildDirectory = path2.join(root, "build");
321
+ if (existsSync2(buildDirectory)) {
322
+ await rimraf(buildDirectory);
323
+ }
324
+ console.log(" \u2714\uFE0E cleaned build directory");
325
+ }
326
+ async function getRootProjectDirectory() {
327
+ const file = await findUp2("package.json", { cwd: process.cwd() });
328
+ if (!file) {
329
+ throw new Error(
330
+ "please run this command inside a whop react native project"
331
+ );
332
+ }
333
+ const root = path2.dirname(file);
334
+ return root;
335
+ }
336
+ main().catch((err) => {
337
+ console.error(err);
338
+ process.exit(1);
339
+ }).then(() => {
340
+ process.exit(0);
341
+ });
342
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/cli/mobile.ts","../../src/cli/file.ts","../../src/cli/sdk.ts","../../src/cli/valid-view-type.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport { findUp } from \"find-up\";\nimport { rimraf } from \"rimraf\";\nimport { buildAndPublish } from \"./mobile\";\n\nasync function main() {\n\tconst args = parseArgs({\n\t\toptions: {\n\t\t\tios: {\n\t\t\t\ttype: \"boolean\",\n\t\t\t},\n\t\t\tandroid: {\n\t\t\t\ttype: \"boolean\",\n\t\t\t},\n\t\t\tweb: {\n\t\t\t\ttype: \"boolean\",\n\t\t\t},\n\t\t},\n\t\tstrict: true,\n\t\tallowPositionals: true,\n\t\targs: process.argv.slice(2),\n\t});\n\n\tconst [command] = args.positionals;\n\n\tlet shouldBuild = true;\n\tlet shouldUpload = true;\n\tlet shouldClean = true;\n\tif (command === \"build\") {\n\t\tshouldUpload = false;\n\t} else if (command === \"ship\") {\n\t\tshouldBuild = true;\n\t\tshouldUpload = true;\n\t} else if (command === \"upload\") {\n\t\tshouldBuild = false;\n\t\tshouldClean = false;\n\t} else if (command === \"clean\") {\n\t\tshouldBuild = false;\n\t\tshouldUpload = false;\n\t} else {\n\t\tconsole.error(\n\t\t\t`Usage:\n\twhop-react-native ship [--ios] [--android] [--web] # runs build and then publishes it as a dev build to whop.\n\twhop-react-native build [--ios] [--android] [--web] # builds your app into a distributable bundle in the build/ directory.\n\twhop-react-native upload [--ios] [--android] [--web] # uploads the existing build directory to whop.\n\twhop-react-native clean # cleans the build directory.`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\tconst root = await getRootProjectDirectory();\n\n\tif (shouldClean) {\n\t\tawait cleanBuildDirectory(root);\n\t}\n\n\tconst didProvidePlatform =\n\t\targs.values.ios || args.values.android || args.values.web;\n\n\tconst opts = { shouldBuild, shouldUpload };\n\tconst promises: Promise<void>[] = [];\n\n\tif (args.values.ios || !didProvidePlatform) {\n\t\tpromises.push(buildAndPublish(root, \"ios\", opts));\n\t}\n\tif (args.values.android || !didProvidePlatform) {\n\t\tpromises.push(buildAndPublish(root, \"android\", opts));\n\t}\n\tif (args.values.web || !didProvidePlatform) {\n\t\tconsole.warn(\" - [web] builds for web are not supported yet - coming soon\");\n\t}\n\n\tawait Promise.all(promises);\n}\n\nasync function cleanBuildDirectory(root: string) {\n\tconst buildDirectory = path.join(root, \"build\");\n\tif (existsSync(buildDirectory)) {\n\t\tawait rimraf(buildDirectory);\n\t}\n\tconsole.log(\" ✔︎ cleaned build directory\");\n}\n\nasync function getRootProjectDirectory() {\n\tconst file = await findUp(\"package.json\", { cwd: process.cwd() });\n\tif (!file) {\n\t\tthrow new Error(\n\t\t\t\"please run this command inside a whop react native project\",\n\t\t);\n\t}\n\tconst root = path.dirname(file);\n\treturn root;\n}\n\nmain()\n\t.catch((err) => {\n\t\tconsole.error(err);\n\t\tprocess.exit(1);\n\t})\n\t.then(() => {\n\t\tprocess.exit(0);\n\t});\n","import { existsSync, readFileSync, readdirSync, statSync } from \"node:fs\";\nimport { mkdir, readdir, rename, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { getDefaultConfig } from \"@react-native/metro-config\";\nimport { findUp } from \"find-up\";\nimport JSZip from \"jszip\";\nimport { type ReportableEvent, type Reporter, runBuild } from \"metro\";\nimport { getChecksum, uploadFile } from \"./file\";\nimport { APP_ID, COMPANY_ID, whopSdk } from \"./sdk\";\nimport { VALID_VIEW_TYPES } from \"./valid-view-type\";\n\nexport async function buildAndPublish(\n\troot: string,\n\tplatform: \"ios\" | \"android\",\n\t{\n\t\tshouldBuild = true,\n\t\tshouldUpload = true,\n\t}: { shouldBuild: boolean; shouldUpload: boolean } = {\n\t\tshouldBuild: true,\n\t\tshouldUpload: true,\n\t},\n) {\n\tif (shouldBuild) {\n\t\tawait bundle(root, platform);\n\t}\n\tif (shouldUpload) {\n\t\tawait createMobileBuild(root, platform);\n\t}\n}\n\nexport async function bundle(root: string, platform: \"ios\" | \"android\") {\n\tawait makeEntrypoint(root, platform);\n\n\tconst outputFile = path.join(\n\t\troot,\n\t\t\"build\",\n\t\t\"output\",\n\t\tplatform,\n\t\t\"main_js_bundle\",\n\t);\n\tawait mkdir(path.dirname(outputFile), { recursive: true });\n\n\tconst defaultConfig = getDefaultConfig(root);\n\n\tconst babelLocation = require.resolve(\"@babel/runtime/package\");\n\n\tconst bableNodeModules = await findUp(\"node_modules\", {\n\t\tcwd: babelLocation,\n\t\ttype: \"directory\",\n\t});\n\tif (!bableNodeModules) {\n\t\tthrow new Error(\"babel node_modules parent folder not found\");\n\t}\n\n\tawait runBuild(\n\t\t{\n\t\t\t...defaultConfig,\n\t\t\tprojectRoot: root,\n\t\t\ttransformer: {\n\t\t\t\t...defaultConfig.transformer,\n\t\t\t\tbabelTransformerPath: require.resolve(\n\t\t\t\t\t\"./whop-react-native-babel-transformer.js\",\n\t\t\t\t),\n\t\t\t},\n\t\t\twatchFolders: [\n\t\t\t\troot,\n\t\t\t\tpath.resolve(root, \"node_modules\"),\n\t\t\t\tbableNodeModules,\n\t\t\t],\n\t\t\treporter: new CustomReporter(),\n\t\t\tresolver: {\n\t\t\t\t...defaultConfig.resolver,\n\t\t\t\tnodeModulesPaths: [\n\t\t\t\t\t...(defaultConfig.resolver?.nodeModulesPaths ?? []),\n\t\t\t\t\tbableNodeModules,\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdev: false,\n\t\t\tentry: `build/entrypoints/${platform}/index.js`,\n\t\t\tminify: false,\n\t\t\tplatform: platform,\n\t\t\tsourceMap: false,\n\t\t\tout: outputFile,\n\t\t},\n\t);\n\n\tawait rename(\n\t\t`${outputFile}.js`,\n\t\tpath.join(root, \"build\", \"output\", platform, \"main_js_bundle.hbc\"),\n\t);\n\n\tconsole.log(` ✔︎ [${platform}] bundle created`);\n}\n\nasync function getSupportedAppViewTypes(\n\troot: string,\n): Promise<(typeof VALID_VIEW_TYPES)[number][]> {\n\tconst views = await readdir(path.join(root, \"src\", \"views\"), {\n\t\twithFileTypes: true,\n\t\trecursive: false,\n\t});\n\tconst files = views\n\t\t.filter((file) => file.isFile())\n\t\t.map((file) => file.name.split(\".\")[0])\n\t\t.filter((file) => !!file);\n\n\tconst validViews = files.filter((file) =>\n\t\tVALID_VIEW_TYPES.includes(file as (typeof VALID_VIEW_TYPES)[number]),\n\t) as (typeof VALID_VIEW_TYPES)[number][];\n\n\tif (validViews.length === 0) {\n\t\tthrow new Error(\n\t\t\t`No valid views found, please create a view in the src/views folder and name it with a valid view type: ${VALID_VIEW_TYPES.join(\", \")}`,\n\t\t);\n\t}\n\n\treturn validViews;\n}\n\nasync function makeEntrypoint(\n\troot: string,\n\tplatform: \"ios\" | \"android\",\n): Promise<string> {\n\tconst entrypoint = path.join(\n\t\troot,\n\t\t\"build\",\n\t\t\"entrypoints\",\n\t\tplatform,\n\t\t\"index.js\",\n\t);\n\n\tconst files = await getSupportedAppViewTypes(root);\n\n\tconst pascalCase = (str: string) =>\n\t\tstr\n\t\t\t.split(\"-\")\n\t\t\t.map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n\t\t\t.join(\"\");\n\n\tconst imports = files.map(\n\t\t(file) =>\n\t\t\t`import { ${pascalCase(file)} } from \"../../../src/views/${file}\";`,\n\t);\n\tconst registry = files.map(\n\t\t(file) =>\n\t\t\t`AppRegistry.registerComponent(\"${pascalCase(file)}\", () => ${pascalCase(file)});`,\n\t);\n\n\tconst entrypointContent = `import { AppRegistry } from \"react-native\";\n${imports.join(\"\\n\")}\n\n${registry.join(\"\\n\")}\n`;\n\n\tconst entrypointDir = path.dirname(entrypoint);\n\tawait mkdir(entrypointDir, { recursive: true });\n\tawait writeFile(entrypoint, entrypointContent, \"utf-8\");\n\n\tconsole.log(` ✔︎ [${platform}] entrypoint created`);\n\n\treturn entrypoint;\n}\n\nexport async function createMobileBuild(\n\troot: string,\n\tplatform: \"ios\" | \"android\",\n) {\n\tconst viewTypes = await getSupportedAppViewTypes(root);\n\n\tconst fullDirectory = path.join(root, \"build\", \"output\", platform);\n\n\t// Check if the directory contains a file called `main_js_bundle.hbc`\n\tconst mainJsBundle = path.join(fullDirectory, \"main_js_bundle.hbc\");\n\n\tif (!existsSync(mainJsBundle)) {\n\t\tthrow new Error(`main_js_bundle.hbc not found in ${fullDirectory}`);\n\t}\n\n\t// check that that folder only contains the main_js_bundle.hbc file and an optional `assets` folder\n\tconst files = readdirSync(fullDirectory);\n\tif (\n\t\tfiles.length > 2 ||\n\t\tfiles.length < 1 ||\n\t\t!files.includes(\"main_js_bundle.hbc\")\n\t) {\n\t\tthrow new Error(\n\t\t\t\"Directory must contain only the main_js_bundle.hbc file and an optional `assets` folder\",\n\t\t);\n\t}\n\tif (files.length === 2 && !files.includes(\"assets\")) {\n\t\tthrow new Error(\n\t\t\t\"Directory must contain only the main_js_bundle.hbc file and an optional `assets` folder\",\n\t\t);\n\t}\n\n\t// Zip the directory\n\tconst zipData = await zipDirectory(fullDirectory);\n\n\tconst checksum = await getChecksum(zipData);\n\n\tconsole.log(` ✔︎ [${platform}] build zipped with checksum: ${checksum}`);\n\n\tconst fileName = `app_build_${platform}.zip`;\n\tconst uploadedFile = await uploadFile(zipData, fileName, \"application/zip\");\n\n\tconsole.log(\n\t\t` ✔︎ [${platform}] uploaded build: ${fileName} (${(zipData.length / 1024).toFixed(0)} KB)`,\n\t);\n\n\tconst build = await whopSdk.apps.createAppBuild({\n\t\tattachment: { directUploadId: uploadedFile.directUploadId },\n\t\tchecksum,\n\t\tplatform,\n\t\tsupportedAppViewTypes: viewTypes.map(\n\t\t\t(view) =>\n\t\t\t\t({\n\t\t\t\t\t\"experience-view\": \"hub\" as const,\n\t\t\t\t\t\"discover-view\": \"discover\" as const,\n\t\t\t\t})[view],\n\t\t),\n\t});\n\n\tif (!build) {\n\t\tthrow new Error(\"Failed to create app build\");\n\t}\n\n\tconst dashboardUrl = `https://whop.com/dashboard/${COMPANY_ID}/developer/apps/${APP_ID}/builds/`;\n\n\tconsole.log(`\\n ✔︎ [${platform}] deployed as development build ✔︎\n - build id: ${build.id}\n - view types: ${viewTypes.join(\", \")}\n - promote to production here: ${dashboardUrl}\\n`);\n\n\treturn build;\n}\n\nasync function zipDirectory(\n\tdirectory: string,\n): Promise<Buffer<ArrayBufferLike>> {\n\tconst zip = new JSZip();\n\n\t// Recursively add files to zip\n\tfunction addFilesToZip(currentPath: string, relativePath = \"\") {\n\t\tconst items = readdirSync(currentPath);\n\n\t\tfor (const item of items) {\n\t\t\tconst fullPath = path.join(currentPath, item);\n\t\t\tconst zipPath = relativePath ? path.join(relativePath, item) : item;\n\t\t\tconst stats = statSync(fullPath);\n\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\taddFilesToZip(fullPath, zipPath);\n\t\t\t} else {\n\t\t\t\tconst fileContent = readFileSync(fullPath);\n\t\t\t\tzip.file(zipPath, fileContent);\n\t\t\t}\n\t\t}\n\t}\n\n\taddFilesToZip(directory);\n\n\t// Generate zip file\n\tconst zipData = await zip.generateAsync({ type: \"nodebuffer\" });\n\n\treturn zipData;\n}\n\nclass CustomReporter implements Reporter {\n\tupdate(event: ReportableEvent) {\n\t\t// Do nothing.\n\t}\n}\n","import { createHash } from \"node:crypto\";\nimport { AGENT_USER_ID, APP_ID, whopSdk } from \"./sdk\";\n\nexport async function uploadFile(\n\tdata: Buffer<ArrayBufferLike>,\n\tname: string,\n\tcontentType: string,\n) {\n\tconst file = new File([data], name, {\n\t\ttype: contentType,\n\t});\n\n\tconst uploadedFile = await whopSdk\n\t\t.withUser(AGENT_USER_ID)\n\t\t.attachments.uploadAttachment({\n\t\t\tfile,\n\t\t\trecord: \"app\",\n\t\t\tid: APP_ID,\n\t\t});\n\n\treturn uploadedFile;\n}\n\nexport async function getChecksum(data: Buffer<ArrayBufferLike>) {\n\tconst hash = createHash(\"sha256\");\n\thash.update(data);\n\treturn hash.digest(\"hex\");\n}\n","import { WhopServerSdk } from \"@whop/api\";\nimport { config } from \"dotenv\";\n\nconfig({\n\tpath: [\".env\", \".env.local\", \".env.development\", \".env.production\"],\n});\n\nfunction env(key: string) {\n\tconst value = process.env[key];\n\tif (!value) {\n\t\tthrow new Error(`Missing environment variable: ${key}`);\n\t}\n\treturn value;\n}\n\nexport const AGENT_USER_ID = env(\"NEXT_PUBLIC_WHOP_AGENT_USER_ID\");\nexport const COMPANY_ID = env(\"NEXT_PUBLIC_WHOP_COMPANY_ID\");\nexport const APP_ID = env(\"NEXT_PUBLIC_WHOP_APP_ID\");\n\nexport const whopSdk: WhopServerSdk = WhopServerSdk({\n\tappApiKey: env(\"WHOP_API_KEY\"),\n\tappId: APP_ID,\n\tcompanyId: COMPANY_ID,\n\tonBehalfOfUserId: AGENT_USER_ID,\n});\n","export const VALID_VIEW_TYPES = [\"experience-view\", \"discover-view\"] as const;\n"],"mappings":"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","names":["existsSync","path","findUp","path","existsSync","findUp"]}
@@ -0,0 +1,17 @@
1
+ // build/metroCustomTransformer.js (or anywhere in your repo)
2
+ const upstream = require("metro-react-native-babel-transformer");
3
+
4
+ /**
5
+ * Metro calls `transform` for every file it processes.
6
+ * We forward the call to the upstream transformer, but
7
+ * force-inject our own Babel config.
8
+ */
9
+ module.exports.transform = ({ options, ...rest }) => {
10
+ return upstream.transform({
11
+ ...rest,
12
+ options: {
13
+ ...options, // keep Metro’s own options
14
+ extendsBabelConfigPath: require.resolve("./babel.config.js"),
15
+ },
16
+ });
17
+ };
@@ -0,0 +1,16 @@
1
+ import { WhopClientSdk } from '@whop/api';
2
+
3
+ declare const whopSdk: WhopClientSdk;
4
+
5
+ interface BaseViewProps {
6
+ currentUserId: string | undefined | null;
7
+ restPath: string | undefined | null;
8
+ }
9
+ interface ExperienceViewProps extends BaseViewProps {
10
+ experienceId: string;
11
+ companyId: string;
12
+ }
13
+ interface DiscoverViewProps extends BaseViewProps {
14
+ }
15
+
16
+ export { type DiscoverViewProps, type ExperienceViewProps, whopSdk };
@@ -0,0 +1,16 @@
1
+ import { WhopClientSdk } from '@whop/api';
2
+
3
+ declare const whopSdk: WhopClientSdk;
4
+
5
+ interface BaseViewProps {
6
+ currentUserId: string | undefined | null;
7
+ restPath: string | undefined | null;
8
+ }
9
+ interface ExperienceViewProps extends BaseViewProps {
10
+ experienceId: string;
11
+ companyId: string;
12
+ }
13
+ interface DiscoverViewProps extends BaseViewProps {
14
+ }
15
+
16
+ export { type DiscoverViewProps, type ExperienceViewProps, whopSdk };
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/lib/index.ts
21
+ var lib_exports = {};
22
+ __export(lib_exports, {
23
+ whopSdk: () => whopSdk
24
+ });
25
+ module.exports = __toCommonJS(lib_exports);
26
+
27
+ // src/lib/client-sdk.ts
28
+ var import_api = require("@whop/api");
29
+ var import_react_native2 = require("react-native");
30
+
31
+ // src/lib/native-whop-core.ts
32
+ var import_react_native = require("react-native");
33
+ var native_whop_core_default = import_react_native.TurboModuleRegistry.getEnforcing("NativeWhopCore");
34
+
35
+ // src/lib/client-sdk.ts
36
+ function getAppOrigin() {
37
+ if (import_react_native2.Platform.OS === "android" || import_react_native2.Platform.OS === "ios") {
38
+ const result = native_whop_core_default.execSync("getAppApiOrigin", "{}");
39
+ if (result.isOk) {
40
+ const { apiOrigin } = JSON.parse(result.data || "{}");
41
+ return apiOrigin;
42
+ }
43
+ throw new Error(`Failed to get app origin: ${result.errorMessage}`);
44
+ }
45
+ if (import_react_native2.Platform.OS === "web" && typeof window !== "undefined") {
46
+ return window.location.origin;
47
+ }
48
+ throw new Error(`Unsupported platform: ${import_react_native2.Platform.OS}`);
49
+ }
50
+ var appOrigin = getAppOrigin();
51
+ var whopSdk = (0, import_api.WhopClientSdk)({
52
+ apiOrigin: appOrigin,
53
+ apiPath: "/_whop/public-graphql/"
54
+ });
55
+ // Annotate the CommonJS export names for ESM import in node:
56
+ 0 && (module.exports = {
57
+ whopSdk
58
+ });
59
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/index.ts","../../src/lib/client-sdk.ts","../../src/lib/native-whop-core.ts"],"sourcesContent":["export * from \"./client-sdk\";\nexport * from \"./props\";\n","import { WhopClientSdk } from \"@whop/api\";\nimport { Platform } from \"react-native\";\nimport nativeWhopCore from \"./native-whop-core\";\n\nfunction getAppOrigin() {\n\tif (Platform.OS === \"android\" || Platform.OS === \"ios\") {\n\t\tconst result = nativeWhopCore.execSync(\"getAppApiOrigin\", \"{}\");\n\t\tif (result.isOk) {\n\t\t\tconst { apiOrigin } = JSON.parse(result.data || \"{}\");\n\t\t\treturn apiOrigin;\n\t\t}\n\t\tthrow new Error(`Failed to get app origin: ${result.errorMessage}`);\n\t}\n\n\tif (Platform.OS === \"web\" && typeof window !== \"undefined\") {\n\t\treturn window.location.origin;\n\t}\n\n\tthrow new Error(`Unsupported platform: ${Platform.OS}`);\n}\n\nconst appOrigin = getAppOrigin();\n\nexport const whopSdk: WhopClientSdk = WhopClientSdk({\n\tapiOrigin: appOrigin,\n\tapiPath: \"/_whop/public-graphql/\",\n});\n","import type { TurboModule } from \"react-native\";\nimport { TurboModuleRegistry } from \"react-native\";\n\nexport type FunctionCallResult = {\n\tisOk: boolean;\n\tdata: string | null;\n\terrorMessage: string | null;\n};\n\nexport interface Spec extends TurboModule {\n\texecSync(name: string, paramsJson: string): FunctionCallResult;\n\texecAsync(name: string, paramsJson: string): Promise<FunctionCallResult>;\n}\n\nexport default TurboModuleRegistry.getEnforcing<Spec>(\"NativeWhopCore\");\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,iBAA8B;AAC9B,IAAAA,uBAAyB;;;ACAzB,0BAAoC;AAapC,IAAO,2BAAQ,wCAAoB,aAAmB,gBAAgB;;;ADVtE,SAAS,eAAe;AACvB,MAAI,8BAAS,OAAO,aAAa,8BAAS,OAAO,OAAO;AACvD,UAAM,SAAS,yBAAe,SAAS,mBAAmB,IAAI;AAC9D,QAAI,OAAO,MAAM;AAChB,YAAM,EAAE,UAAU,IAAI,KAAK,MAAM,OAAO,QAAQ,IAAI;AACpD,aAAO;AAAA,IACR;AACA,UAAM,IAAI,MAAM,6BAA6B,OAAO,YAAY,EAAE;AAAA,EACnE;AAEA,MAAI,8BAAS,OAAO,SAAS,OAAO,WAAW,aAAa;AAC3D,WAAO,OAAO,SAAS;AAAA,EACxB;AAEA,QAAM,IAAI,MAAM,yBAAyB,8BAAS,EAAE,EAAE;AACvD;AAEA,IAAM,YAAY,aAAa;AAExB,IAAM,cAAyB,0BAAc;AAAA,EACnD,WAAW;AAAA,EACX,SAAS;AACV,CAAC;","names":["import_react_native"]}
@@ -0,0 +1,34 @@
1
+ import "../chunk-BJTO5JO5.mjs";
2
+
3
+ // src/lib/client-sdk.ts
4
+ import { WhopClientSdk } from "@whop/api";
5
+ import { Platform } from "react-native";
6
+
7
+ // src/lib/native-whop-core.ts
8
+ import { TurboModuleRegistry } from "react-native";
9
+ var native_whop_core_default = TurboModuleRegistry.getEnforcing("NativeWhopCore");
10
+
11
+ // src/lib/client-sdk.ts
12
+ function getAppOrigin() {
13
+ if (Platform.OS === "android" || Platform.OS === "ios") {
14
+ const result = native_whop_core_default.execSync("getAppApiOrigin", "{}");
15
+ if (result.isOk) {
16
+ const { apiOrigin } = JSON.parse(result.data || "{}");
17
+ return apiOrigin;
18
+ }
19
+ throw new Error(`Failed to get app origin: ${result.errorMessage}`);
20
+ }
21
+ if (Platform.OS === "web" && typeof window !== "undefined") {
22
+ return window.location.origin;
23
+ }
24
+ throw new Error(`Unsupported platform: ${Platform.OS}`);
25
+ }
26
+ var appOrigin = getAppOrigin();
27
+ var whopSdk = WhopClientSdk({
28
+ apiOrigin: appOrigin,
29
+ apiPath: "/_whop/public-graphql/"
30
+ });
31
+ export {
32
+ whopSdk
33
+ };
34
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/client-sdk.ts","../../src/lib/native-whop-core.ts"],"sourcesContent":["import { WhopClientSdk } from \"@whop/api\";\nimport { Platform } from \"react-native\";\nimport nativeWhopCore from \"./native-whop-core\";\n\nfunction getAppOrigin() {\n\tif (Platform.OS === \"android\" || Platform.OS === \"ios\") {\n\t\tconst result = nativeWhopCore.execSync(\"getAppApiOrigin\", \"{}\");\n\t\tif (result.isOk) {\n\t\t\tconst { apiOrigin } = JSON.parse(result.data || \"{}\");\n\t\t\treturn apiOrigin;\n\t\t}\n\t\tthrow new Error(`Failed to get app origin: ${result.errorMessage}`);\n\t}\n\n\tif (Platform.OS === \"web\" && typeof window !== \"undefined\") {\n\t\treturn window.location.origin;\n\t}\n\n\tthrow new Error(`Unsupported platform: ${Platform.OS}`);\n}\n\nconst appOrigin = getAppOrigin();\n\nexport const whopSdk: WhopClientSdk = WhopClientSdk({\n\tapiOrigin: appOrigin,\n\tapiPath: \"/_whop/public-graphql/\",\n});\n","import type { TurboModule } from \"react-native\";\nimport { TurboModuleRegistry } from \"react-native\";\n\nexport type FunctionCallResult = {\n\tisOk: boolean;\n\tdata: string | null;\n\terrorMessage: string | null;\n};\n\nexport interface Spec extends TurboModule {\n\texecSync(name: string, paramsJson: string): FunctionCallResult;\n\texecAsync(name: string, paramsJson: string): Promise<FunctionCallResult>;\n}\n\nexport default TurboModuleRegistry.getEnforcing<Spec>(\"NativeWhopCore\");\n"],"mappings":";;;AAAA,SAAS,qBAAqB;AAC9B,SAAS,gBAAgB;;;ACAzB,SAAS,2BAA2B;AAapC,IAAO,2BAAQ,oBAAoB,aAAmB,gBAAgB;;;ADVtE,SAAS,eAAe;AACvB,MAAI,SAAS,OAAO,aAAa,SAAS,OAAO,OAAO;AACvD,UAAM,SAAS,yBAAe,SAAS,mBAAmB,IAAI;AAC9D,QAAI,OAAO,MAAM;AAChB,YAAM,EAAE,UAAU,IAAI,KAAK,MAAM,OAAO,QAAQ,IAAI;AACpD,aAAO;AAAA,IACR;AACA,UAAM,IAAI,MAAM,6BAA6B,OAAO,YAAY,EAAE;AAAA,EACnE;AAEA,MAAI,SAAS,OAAO,SAAS,OAAO,WAAW,aAAa;AAC3D,WAAO,OAAO,SAAS;AAAA,EACxB;AAEA,QAAM,IAAI,MAAM,yBAAyB,SAAS,EAAE,EAAE;AACvD;AAEA,IAAM,YAAY,aAAa;AAExB,IAAM,UAAyB,cAAc;AAAA,EACnD,WAAW;AAAA,EACX,SAAS;AACV,CAAC;","names":[]}
package/license.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Whop, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@whop/react-native",
3
+ "description": "React Native SDK for building embedded apps on Whop",
4
+ "version": "0.0.2",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/whopio/whop-sdk-ts",
8
+ "directory": "packages/react-native"
9
+ },
10
+ "keywords": [
11
+ "whop",
12
+ "react-native",
13
+ "sdk",
14
+ "embedded",
15
+ "apps",
16
+ "payments"
17
+ ],
18
+ "bugs": "https://github.com/whopio/whop-sdk-ts/issues",
19
+ "homepage": "https://whop.com/developers/",
20
+ "license": "MIT",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/lib/index.d.ts",
24
+ "import": "./dist/lib/index.mjs",
25
+ "require": "./dist/lib/index.cjs"
26
+ }
27
+ },
28
+ "main": "./dist/lib/index.cjs",
29
+ "module": "./dist/lib/index.mjs",
30
+ "types": "./dist/lib/index.d.ts",
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "bin": {
35
+ "whop-react-native": "./dist/cli/index.js"
36
+ },
37
+ "dependencies": {
38
+ "@babel/core": "^7.28.0",
39
+ "@babel/plugin-transform-export-namespace-from": "^7.27.1",
40
+ "@babel/preset-env": "^7.28.0",
41
+ "@babel/runtime": "^7.27.6",
42
+ "@react-native/babel-preset": "^0.80.1",
43
+ "@react-native/metro-config": "^0.80.1",
44
+ "dotenv": "16.5.0",
45
+ "events": "^3.3.0",
46
+ "find-up": "^7.0.0",
47
+ "jszip": "^3.10.1",
48
+ "metro": "^0.83.0",
49
+ "metro-react-native-babel-transformer": "^0.77.0",
50
+ "rimraf": "^6.0.1",
51
+ "@whop/api": "0.0.36"
52
+ },
53
+ "devDependencies": {
54
+ "@types/node": "latest",
55
+ "@types/react": "19.1.5",
56
+ "react": "^19.0.0",
57
+ "react-native": "0.80.0",
58
+ "tsup": "8.5.0",
59
+ "typescript": "latest"
60
+ },
61
+ "engines": {
62
+ "node": "22.x",
63
+ "pnpm": "9.15.9"
64
+ },
65
+ "publishConfig": {
66
+ "access": "public"
67
+ },
68
+ "peerDependencies": {
69
+ "react": "^19.0.0",
70
+ "react-native": "0.80.0"
71
+ },
72
+ "scripts": {
73
+ "build": "pnpm run clean && tsup && cp src/cli/*.js dist/cli",
74
+ "clean": "rm -rf dist",
75
+ "dev": "tsup --watch",
76
+ "check-types": "tsc --noEmit",
77
+ "lint:fix": "biome check --write --unsafe"
78
+ }
79
+ }