@supertokens-plugins/rownd-nodejs 0.2.1 → 0.3.0-beta.0

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.
@@ -0,0 +1,731 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // scripts/generateAppConfig.ts
32
+ var generateAppConfig_exports = {};
33
+ __export(generateAppConfig_exports, {
34
+ convertRowndConfigToPluginConfig: () => convertRowndConfigToPluginConfig
35
+ });
36
+ module.exports = __toCommonJS(generateAppConfig_exports);
37
+ var fs2 = __toESM(require("fs/promises"));
38
+ var import_node_path2 = require("path");
39
+
40
+ // scripts/scriptUtils.ts
41
+ var fs = __toESM(require("fs/promises"));
42
+ var import_node_path = require("path");
43
+ var import_yaml = require("yaml");
44
+ var import_zod = require("zod");
45
+ var SCRIPT_DIR = __dirname;
46
+ var ConfigSchema = import_zod.z.object({
47
+ limit: import_zod.z.preprocess(
48
+ (value) => value === void 0 ? Number.POSITIVE_INFINITY : value,
49
+ import_zod.z.union([import_zod.z.literal(Number.POSITIVE_INFINITY), import_zod.z.number().int().positive()])
50
+ ),
51
+ checkpoint: import_zod.z.object({
52
+ file: import_zod.z.string(),
53
+ resume: import_zod.z.boolean().default(false)
54
+ }),
55
+ retry: import_zod.z.object({
56
+ maxAttempts: import_zod.z.number().int().positive(),
57
+ initialDelayMs: import_zod.z.number().int().positive()
58
+ }),
59
+ rownd: import_zod.z.object({
60
+ appId: import_zod.z.string(),
61
+ appKey: import_zod.z.string(),
62
+ appSecret: import_zod.z.string(),
63
+ bearerToken: import_zod.z.string().optional(),
64
+ pageSize: import_zod.z.number().int().positive()
65
+ }),
66
+ supertokens: import_zod.z.object({
67
+ connectionURI: import_zod.z.string(),
68
+ apiKey: import_zod.z.string().optional(),
69
+ batchSize: import_zod.z.number().int().positive()
70
+ }),
71
+ thirdPartyProviders: import_zod.z.array(
72
+ import_zod.z.object({
73
+ thirdPartyId: import_zod.z.string(),
74
+ name: import_zod.z.string().optional(),
75
+ clients: import_zod.z.array(
76
+ import_zod.z.object({
77
+ clientType: import_zod.z.string().optional(),
78
+ clientId: import_zod.z.string(),
79
+ clientSecret: import_zod.z.string().optional(),
80
+ scope: import_zod.z.array(import_zod.z.string()).optional()
81
+ })
82
+ )
83
+ })
84
+ ).optional()
85
+ });
86
+ var RowndUserRecordSchema = import_zod.z.looseObject({
87
+ user_id: import_zod.z.string().optional(),
88
+ rownd_user: import_zod.z.string().optional(),
89
+ subject: import_zod.z.string().optional(),
90
+ state: import_zod.z.string().optional(),
91
+ auth_level: import_zod.z.string().optional(),
92
+ data: import_zod.z.looseObject({
93
+ user_id: import_zod.z.string(),
94
+ email: import_zod.z.string().optional(),
95
+ phone_number: import_zod.z.string().optional(),
96
+ google_id: import_zod.z.string().optional(),
97
+ apple_id: import_zod.z.string().optional(),
98
+ first_name: import_zod.z.string().optional(),
99
+ last_name: import_zod.z.string().optional()
100
+ }),
101
+ verified_data: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).default({}),
102
+ attributes: import_zod.z.record(import_zod.z.string(), import_zod.z.array(import_zod.z.string())).optional(),
103
+ groups: import_zod.z.array(import_zod.z.unknown()).optional(),
104
+ meta: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional(),
105
+ connection_map: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
106
+ });
107
+ var RowndUserListItemSchema = import_zod.z.object({
108
+ data: RowndUserRecordSchema
109
+ });
110
+ var RowndUsersPageSchema = import_zod.z.object({
111
+ results: import_zod.z.array(RowndUserRecordSchema).optional(),
112
+ data: import_zod.z.array(RowndUserListItemSchema).optional(),
113
+ total_results: import_zod.z.number().optional()
114
+ }).superRefine((value, context) => {
115
+ if (!value.results && !value.data) {
116
+ context.addIssue({
117
+ code: import_zod.z.ZodIssueCode.custom,
118
+ message: "Rownd API response is missing a results/data array."
119
+ });
120
+ }
121
+ });
122
+ function parseConfig(rawConfig, configDir = SCRIPT_DIR) {
123
+ const parsed = ConfigSchema.parse(rawConfig);
124
+ const checkpointFile = (0, import_node_path.isAbsolute)(parsed.checkpoint.file) ? parsed.checkpoint.file : (0, import_node_path.resolve)(configDir, parsed.checkpoint.file);
125
+ return {
126
+ limit: parsed.limit,
127
+ checkpoint: {
128
+ file: checkpointFile,
129
+ resume: parsed.checkpoint.resume
130
+ },
131
+ retry: parsed.retry,
132
+ rownd: parsed.rownd,
133
+ supertokens: parsed.supertokens,
134
+ thirdPartyProviders: parsed.thirdPartyProviders
135
+ };
136
+ }
137
+ async function loadConfig(configFilePath) {
138
+ const configFile = await fs.readFile(configFilePath, "utf8");
139
+ return parseConfig((0, import_yaml.parse)(configFile), (0, import_node_path.dirname)(configFilePath));
140
+ }
141
+ function parseRequiredConfigArg(args) {
142
+ for (let i = 0; i < args.length; i += 1) {
143
+ const arg = args[i];
144
+ if (arg === "--config" || arg === "-c") {
145
+ const value = args[i + 1];
146
+ if (!value) {
147
+ throw new Error(`Missing value for ${arg}`);
148
+ }
149
+ return (0, import_node_path.resolve)(value);
150
+ }
151
+ }
152
+ throw new Error("Missing required --config <path>");
153
+ }
154
+ function hasHelpArg(args) {
155
+ return args.includes("--help") || args.includes("-h");
156
+ }
157
+ function isRetryableStatus(status) {
158
+ return status === 429 || status >= 500;
159
+ }
160
+ async function fetchWithRetry({
161
+ url,
162
+ requestInit,
163
+ retryConfig,
164
+ operation
165
+ }) {
166
+ let lastError;
167
+ for (let attempt = 1; attempt <= retryConfig.maxAttempts; attempt += 1) {
168
+ try {
169
+ const response = await fetch(url, requestInit);
170
+ if (response.ok || !isRetryableStatus(response.status) || attempt === retryConfig.maxAttempts) {
171
+ return response;
172
+ }
173
+ const responseText = await response.text();
174
+ lastError = new Error(
175
+ `${operation} failed with ${response.status}${responseText ? ` ${responseText}` : ""}`
176
+ );
177
+ } catch (error) {
178
+ if (attempt === retryConfig.maxAttempts) {
179
+ throw error;
180
+ }
181
+ lastError = error instanceof Error ? error : new Error(`${operation} failed`);
182
+ }
183
+ const delayMs = Math.round(
184
+ retryConfig.initialDelayMs * 2 ** (attempt - 1) * (1 + Math.random() * 0.2)
185
+ );
186
+ console.log(
187
+ `${operation} attempt ${attempt} failed. Retrying in ${delayMs}ms.`
188
+ );
189
+ await new Promise((resolve3) => setTimeout(resolve3, delayMs));
190
+ }
191
+ throw lastError ?? new Error(`${operation} failed`);
192
+ }
193
+
194
+ // scripts/generateAppConfig.ts
195
+ function printHelp() {
196
+ console.log(`Usage: rownd-nodejs generate-plugin-config --config <path> [options]
197
+
198
+ Options:
199
+ -c, --config <path> Path to the bulk migration config file
200
+ -o, --output <path> Destination path for generated plugin config
201
+ --raw-output <path> Also write the raw Rownd app config response to this path
202
+ --include-sub-brands Fetch and include Rownd sub-brands. Requires rownd.bearerToken.
203
+ -h, --help Show this help message`);
204
+ }
205
+ function getRowndApiAuthHeaders(config) {
206
+ if (config.rownd.bearerToken) {
207
+ return { Authorization: `Bearer ${config.rownd.bearerToken}` };
208
+ }
209
+ return {
210
+ "x-rownd-app-key": config.rownd.appKey,
211
+ "x-rownd-app-secret": config.rownd.appSecret
212
+ };
213
+ }
214
+ function convertRowndConfigToPluginConfig(rowndApp, creds) {
215
+ const config = getObject(rowndApp.config);
216
+ const customizations = getObject(config.customizations);
217
+ const hub = getObject(config.hub);
218
+ const hubCustomizations = getObject(hub.customizations);
219
+ const hubAuth = getObject(hub.auth);
220
+ const legal = getObject(hub.legal);
221
+ const customContent = getObject(hub.custom_content);
222
+ const profile = getObject(hub.profile);
223
+ const instructions = [];
224
+ const authTokens = getObject(getObject(config.auth).access_tokens);
225
+ const appConfig = {
226
+ id: getString(rowndApp.id),
227
+ name: getString(rowndApp.name),
228
+ icon: getString(rowndApp.icon),
229
+ userVerificationFields: parseStringArray(rowndApp.user_verification_fields),
230
+ branding: {
231
+ primaryColor: getString(customizations.primary_color),
232
+ primaryColorDarkMode: getString(
233
+ hubCustomizations.primary_color_dark_mode
234
+ ),
235
+ logo: getString(customizations.logo),
236
+ logoDarkMode: getString(customizations.logo_dark_mode),
237
+ animations: getOptionalObject(customizations.animations),
238
+ roundedCorners: getBoolean(hubCustomizations.rounded_corners),
239
+ containerBorderRadius: getNumber(
240
+ hubCustomizations.container_border_radius
241
+ ),
242
+ placement: getString(hubCustomizations.placement),
243
+ hubPrimaryColor: getString(hubCustomizations.primary_color),
244
+ backgroundColor: getString(hubCustomizations.background_color),
245
+ fontFamily: getString(hubCustomizations.font_family),
246
+ hideVerificationIcons: getBoolean(
247
+ hubCustomizations.hide_verification_icons
248
+ ),
249
+ visualSwoops: getBoolean(hubCustomizations.visual_swoops),
250
+ blurBackground: getBoolean(hubCustomizations.blur_background),
251
+ blurBackgroundOpacity: getNumber(
252
+ hubCustomizations.blur_background_opacity
253
+ ),
254
+ offsetX: getNumber(hubCustomizations.offset_x),
255
+ offsetY: getNumber(hubCustomizations.offset_y),
256
+ propertyOverrides: getOptionalObject(hubCustomizations.property_overrides),
257
+ darkMode: parseDarkMode(hubCustomizations.dark_mode),
258
+ showAppIcon: getBoolean(hubAuth.show_app_icon),
259
+ customScripts: parseCustomScripts(hub.custom_scripts),
260
+ customStyles: parseCustomStyles(hub.custom_styles)
261
+ },
262
+ capabilities: getOptionalObject(config.capabilities),
263
+ web: getOptionalObject(config.web),
264
+ bottomSheet: getOptionalObject(config.bottom_sheet),
265
+ profileStorageVersion: getString(config.profile_storage_version),
266
+ allowedWebOrigins: parseStringArray(hub.allowed_web_origins),
267
+ legal: {
268
+ companyName: getString(legal.company_name),
269
+ privacyPolicyUrl: getString(legal.privacy_policy_url),
270
+ termsConditionsUrl: getString(legal.terms_conditions_url),
271
+ supportEmail: getString(legal.support_email)
272
+ },
273
+ customContent: {
274
+ signInModal: isRecord(customContent.sign_in_modal) ? {
275
+ title: getString(customContent.sign_in_modal.title),
276
+ subtitle: getString(customContent.sign_in_modal.subtitle),
277
+ signInTitle: getString(customContent.sign_in_modal.sign_in_title),
278
+ signUpTitle: getString(customContent.sign_in_modal.sign_up_title),
279
+ signInSubtitle: getString(
280
+ customContent.sign_in_modal.sign_in_subtitle
281
+ ),
282
+ signUpSubtitle: getString(
283
+ customContent.sign_in_modal.sign_up_subtitle
284
+ ),
285
+ signInButton: getString(customContent.sign_in_modal.sign_in_button),
286
+ signUpButton: getString(customContent.sign_in_modal.sign_up_button)
287
+ } : void 0,
288
+ profileModal: isRecord(customContent.profile_modal) ? { title: getString(customContent.profile_modal.title) } : void 0,
289
+ verificationModal: isRecord(customContent.verification_modal) ? {
290
+ title: getString(customContent.verification_modal.title),
291
+ subtitle: getString(customContent.verification_modal.subtitle)
292
+ } : void 0,
293
+ signInFailureModal: isRecord(customContent.sign_in_failure_modal) ? {
294
+ failureMessage: getString(
295
+ customContent.sign_in_failure_modal.failure_message
296
+ )
297
+ } : void 0,
298
+ noAccountMessage: isRecord(customContent.no_account_message) ? { title: getString(customContent.no_account_message.title) } : void 0,
299
+ mobile: getOptionalObject(customContent.mobile)
300
+ },
301
+ profile: {
302
+ accountInformation: getObject(profile.account_information),
303
+ personalInformation: getObject(profile.personal_information),
304
+ preferences: getObject(profile.preferences),
305
+ signOutButton: getObject(profile.sign_out_button),
306
+ deleteAccountButton: getObject(profile.delete_account_button),
307
+ addSignInMethodsButton: getObject(profile.add_sign_in_methods_button)
308
+ },
309
+ auth: {
310
+ additionalFields: parseAdditionalFields(hubAuth.additional_fields),
311
+ rememberSignInMethod: getBoolean(hubAuth.remember_sign_in_method),
312
+ useExplicitSignUpFlow: getBoolean(hubAuth.use_explicit_sign_up_flow),
313
+ allowUnverifiedUsers: getBoolean(hubAuth.allow_unverified_users),
314
+ email: parseAuthEmailConfig(hubAuth.email),
315
+ mobile: parseAuthMobileConfig(hubAuth.mobile),
316
+ primarySignUpMethod: getString(hubAuth.primary_sign_up_method),
317
+ preferredMethod: getString(hubAuth.preferred_method),
318
+ order: parseAuthOrder(hubAuth.order)
319
+ },
320
+ signInMethods: (() => {
321
+ const methods = getObject(hubAuth.sign_in_methods);
322
+ const instantUser = getObject(hubAuth.instant_user);
323
+ const result = [];
324
+ for (const [key, value] of Object.entries(methods)) {
325
+ if (!value || !value.enabled) continue;
326
+ if (key === "google") {
327
+ result.push({
328
+ method: "google",
329
+ clientId: value.client_id,
330
+ iosClientId: value.ios_client_id,
331
+ scopes: value.scopes,
332
+ signInFasterWithGoogle: parseSignInFasterWithGoogle(
333
+ value.sign_in_faster_with_google
334
+ ),
335
+ oneTap: value.one_tap ? {
336
+ browser: value.one_tap.browser ? {
337
+ autoPrompt: value.one_tap.browser.auto_prompt,
338
+ delay: value.one_tap.browser.delay
339
+ } : void 0,
340
+ mobileApp: value.one_tap.mobile_app ? {
341
+ autoPrompt: value.one_tap.mobile_app.auto_prompt,
342
+ delay: value.one_tap.mobile_app.delay
343
+ } : void 0
344
+ } : void 0
345
+ });
346
+ instructions.push(
347
+ `Google sign-in was enabled. Please ensure you configure the "google" provider in your SuperTokens ThirdParty recipe. You will need to manually provide the Google 'clientSecret' as Rownd cannot export it.`
348
+ );
349
+ } else if (key === "apple") {
350
+ result.push({
351
+ method: "apple",
352
+ clientId: value.client_id
353
+ });
354
+ instructions.push(
355
+ `Apple sign-in was enabled. Please ensure you configure the "apple" provider in your SuperTokens ThirdParty recipe. You will need to manually provide the Apple 'clientSecret' (or private key) as Rownd cannot export it.`
356
+ );
357
+ } else if (key === "email" || key === "phone") {
358
+ result.push({ method: key });
359
+ instructions.push(
360
+ `${key} sign-in was enabled. Please ensure you configure the SuperTokens Passwordless recipe with the appropriate contactMethod.`
361
+ );
362
+ } else if (key === "anonymous") {
363
+ result.push({
364
+ method: "anonymous",
365
+ type: parseAnonymousType(value.type) ?? "guest",
366
+ displayName: value.display_name,
367
+ iconLightUrl: value.icon_light_url,
368
+ iconDarkUrl: value.icon_dark_url
369
+ });
370
+ } else {
371
+ result.push({
372
+ method: key,
373
+ displayName: value.display_name,
374
+ iconLightUrl: value.icon_light_url,
375
+ iconDarkUrl: value.icon_dark_url
376
+ });
377
+ instructions.push(
378
+ `Custom provider '${key}' was enabled. Please ensure you configure this provider in your SuperTokens ThirdParty recipe. You will need to manually provide the 'clientSecret' as Rownd cannot export it.`
379
+ );
380
+ }
381
+ }
382
+ if (!methods.anonymous?.enabled && getBoolean(instantUser.enabled)) {
383
+ result.push({
384
+ method: "anonymous",
385
+ type: "instant"
386
+ });
387
+ }
388
+ return result;
389
+ })()
390
+ };
391
+ const cleanSchema = {};
392
+ if (isRecord(rowndApp.schema)) {
393
+ for (const [key, field] of Object.entries(
394
+ rowndApp.schema
395
+ )) {
396
+ cleanSchema[key] = {
397
+ display_name: field.display_name,
398
+ type: field.type,
399
+ user_visible: field.user_visible,
400
+ owned_by: field.owned_by,
401
+ read_only: field.read_only,
402
+ show_empty: field.show_empty
403
+ };
404
+ const schemaField = cleanSchema[key];
405
+ if (schemaField) {
406
+ for (const fieldKey of Object.keys(schemaField)) {
407
+ if (schemaField[fieldKey] === void 0) {
408
+ delete schemaField[fieldKey];
409
+ }
410
+ }
411
+ }
412
+ }
413
+ }
414
+ const customClaims = getObject(authTokens.custom_claims);
415
+ for (const [claimName, claimConfig] of Object.entries(
416
+ customClaims
417
+ )) {
418
+ if (claimConfig.source !== "user_profile") {
419
+ instructions.push(
420
+ `Custom claim '${claimName}' uses source '${claimConfig.source ?? "unknown"}'. Please configure this claim manually in SuperTokens.`
421
+ );
422
+ continue;
423
+ }
424
+ if (typeof claimConfig.value !== "string") {
425
+ instructions.push(
426
+ `Custom claim '${claimName}' references a non-string user profile field. Please configure this claim manually in SuperTokens.`
427
+ );
428
+ continue;
429
+ }
430
+ const schemaField = cleanSchema[claimConfig.value];
431
+ if (!schemaField) {
432
+ instructions.push(
433
+ `Custom claim '${claimName}' references user profile field '${claimConfig.value}', but that field was not found in the exported Rownd schema. Please configure this claim manually in SuperTokens.`
434
+ );
435
+ continue;
436
+ }
437
+ schemaField.include_in_session_claims = true;
438
+ if (claimName !== claimConfig.value) {
439
+ schemaField.session_claim_name = claimName;
440
+ }
441
+ }
442
+ const cleanAppConfig = JSON.parse(JSON.stringify(appConfig));
443
+ return {
444
+ rowndAppKey: creds.appKey,
445
+ rowndAppSecret: creds.appSecret,
446
+ schema: cleanSchema,
447
+ appConfig: cleanAppConfig,
448
+ ...instructions.length > 0 ? { _instructions: instructions } : {}
449
+ };
450
+ }
451
+ function mergeRowndAppWithVariant(rowndApp, variant) {
452
+ return deepMerge(rowndApp, {
453
+ name: variant.name,
454
+ config: variant.config
455
+ });
456
+ }
457
+ function deepMerge(base, overlay) {
458
+ const result = { ...base };
459
+ for (const [key, value] of Object.entries(overlay)) {
460
+ if (value === void 0) {
461
+ continue;
462
+ }
463
+ const existingValue = result[key];
464
+ if (isRecord(existingValue) && isRecord(value)) {
465
+ result[key] = deepMerge(existingValue, value);
466
+ } else {
467
+ result[key] = value;
468
+ }
469
+ }
470
+ return result;
471
+ }
472
+ async function fetchRowndVariants(config) {
473
+ const url = new URL(
474
+ `/api/applications/${config.rownd.appId}/variants`,
475
+ "https://app.rownd.io"
476
+ );
477
+ console.log(`Fetching Rownd sub-brands for app_id: ${config.rownd.appId}...`);
478
+ const response = await fetchWithRetry({
479
+ url: url.toString(),
480
+ requestInit: {
481
+ headers: getRowndApiAuthHeaders(config)
482
+ },
483
+ retryConfig: config.retry,
484
+ operation: "Fetching Rownd sub-brands"
485
+ });
486
+ if (!response.ok) {
487
+ if (response.status === 401 && !config.rownd.bearerToken) {
488
+ throw new Error(
489
+ "Rownd variants API returned 401. Add rownd.bearerToken to config.yaml; this endpoint requires Rownd API bearer authorization."
490
+ );
491
+ }
492
+ throw new Error(
493
+ `Rownd variants API error: ${response.status} ${await response.text()}`
494
+ );
495
+ }
496
+ const rawVariants = await response.json();
497
+ const variants = Array.isArray(rawVariants) ? rawVariants : isRecord(rawVariants) && Array.isArray(rawVariants.results) ? rawVariants.results : [];
498
+ return variants.flatMap((variant) => {
499
+ if (!isRecord(variant) || typeof variant.id !== "string") {
500
+ return [];
501
+ }
502
+ return [
503
+ {
504
+ id: variant.id,
505
+ name: getString(variant.name),
506
+ config: getObject(variant.config)
507
+ }
508
+ ];
509
+ });
510
+ }
511
+ function getObject(value) {
512
+ return isRecord(value) ? value : {};
513
+ }
514
+ function getOptionalObject(value) {
515
+ return isRecord(value) ? value : void 0;
516
+ }
517
+ function getString(value) {
518
+ return typeof value === "string" ? value : void 0;
519
+ }
520
+ function getBoolean(value) {
521
+ return typeof value === "boolean" ? value : void 0;
522
+ }
523
+ function getNumber(value) {
524
+ return typeof value === "number" ? value : void 0;
525
+ }
526
+ function parseDarkMode(value) {
527
+ return value === "auto" || value === "light" || value === "dark" ? value : void 0;
528
+ }
529
+ function parseCustomStyles(value) {
530
+ if (!Array.isArray(value)) {
531
+ return void 0;
532
+ }
533
+ return value.flatMap((entry) => {
534
+ if (!isRecord(entry) || typeof entry.content !== "string") {
535
+ return [];
536
+ }
537
+ return [{ content: entry.content }];
538
+ });
539
+ }
540
+ function parseCustomScripts(value) {
541
+ if (!Array.isArray(value)) {
542
+ return void 0;
543
+ }
544
+ return value.flatMap((entry) => {
545
+ if (!isRecord(entry) || typeof entry.content !== "string") {
546
+ return [];
547
+ }
548
+ return [{
549
+ content: entry.content,
550
+ type: getString(entry.type)
551
+ }];
552
+ });
553
+ }
554
+ function parseStringArray(value) {
555
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : void 0;
556
+ }
557
+ function parseAuthEmailConfig(value) {
558
+ if (!isRecord(value)) {
559
+ return void 0;
560
+ }
561
+ return {
562
+ fromAddress: getString(value.from_address),
563
+ image: getString(value.image),
564
+ subject: getString(value.subject),
565
+ callToActionText: getString(value.call_to_action_text),
566
+ verifyTemplate: getString(value.verify_template),
567
+ customContent: getString(value.custom_content),
568
+ customClosingContent: getString(value.custom_closing_content)
569
+ };
570
+ }
571
+ function parseAuthMobileConfig(value) {
572
+ if (!isRecord(value)) {
573
+ return void 0;
574
+ }
575
+ return {
576
+ title: getString(value.title),
577
+ image: getString(value.image),
578
+ callToActionText: getString(value.call_to_action_text),
579
+ hyperlinkText: getString(value.hyperlink_text),
580
+ hyperlinkRedirectUrl: getString(value.hyperlink_redirect_url),
581
+ customContent: getString(value.custom_content)
582
+ };
583
+ }
584
+ function parseAdditionalFields(value) {
585
+ return Array.isArray(value) ? value : void 0;
586
+ }
587
+ function parseAuthOrder(value) {
588
+ return isRecord(value) ? value : void 0;
589
+ }
590
+ function parseAnonymousType(value) {
591
+ return value === "guest" || value === "instant" ? value : void 0;
592
+ }
593
+ function parseSignInFasterWithGoogle(value) {
594
+ return value === "enabled" || value === "disabled" ? value : void 0;
595
+ }
596
+ function isRecord(value) {
597
+ return typeof value === "object" && value !== null && !Array.isArray(value);
598
+ }
599
+ async function run() {
600
+ const args = process.argv.slice(2);
601
+ if (hasHelpArg(args)) {
602
+ printHelp();
603
+ return;
604
+ }
605
+ const config = await loadConfig(parseRequiredConfigArg(args));
606
+ if (!config.rownd.appId || !config.rownd.appKey || !config.rownd.appSecret) {
607
+ throw new Error(
608
+ "Missing rownd.appId, rownd.appKey, or rownd.appSecret in config.yaml"
609
+ );
610
+ }
611
+ const url = new URL("/hub/app-config", "https://api.rownd.io");
612
+ url.searchParams.set("app_id", config.rownd.appId);
613
+ console.log(`Fetching Rownd app config for app_id: ${config.rownd.appId}...`);
614
+ const response = await fetchWithRetry({
615
+ url: url.toString(),
616
+ requestInit: {
617
+ headers: {
618
+ "x-rownd-app-key": config.rownd.appKey,
619
+ "x-rownd-app-secret": config.rownd.appSecret
620
+ }
621
+ },
622
+ retryConfig: config.retry,
623
+ operation: "Fetching Rownd App Config"
624
+ });
625
+ if (!response.ok) {
626
+ throw new Error(
627
+ `Rownd API error: ${response.status} ${await response.text()}`
628
+ );
629
+ }
630
+ const rawConfig = await response.json();
631
+ if (!isRecord(rawConfig) || !isRecord(rawConfig.app)) {
632
+ throw new Error("Rownd API response is missing the 'app' property.");
633
+ }
634
+ const rawOutputPath = parseRawOutputArg(args);
635
+ if (rawOutputPath) {
636
+ const resolvedRawOutputPath = (0, import_node_path2.resolve)(rawOutputPath);
637
+ await fs2.writeFile(
638
+ resolvedRawOutputPath,
639
+ JSON.stringify(rawConfig, null, 2),
640
+ "utf8"
641
+ );
642
+ console.log(`Successfully saved raw Rownd config to ${resolvedRawOutputPath}`);
643
+ }
644
+ const pluginConfig = convertRowndConfigToPluginConfig(rawConfig.app, {
645
+ appKey: config.rownd.appKey,
646
+ appSecret: config.rownd.appSecret
647
+ });
648
+ const subBrands = {};
649
+ if (hasIncludeSubBrandsArg(args)) {
650
+ const variants = await fetchRowndVariants(config);
651
+ for (const variant of variants) {
652
+ const variantPluginConfig = convertRowndConfigToPluginConfig(
653
+ mergeRowndAppWithVariant(rawConfig.app, variant),
654
+ {
655
+ appKey: config.rownd.appKey,
656
+ appSecret: config.rownd.appSecret
657
+ }
658
+ );
659
+ if (!variantPluginConfig.appConfig) {
660
+ continue;
661
+ }
662
+ subBrands[variant.id] = {
663
+ ...variantPluginConfig.appConfig,
664
+ variant: {
665
+ id: variant.id,
666
+ name: variant.name,
667
+ config: variant.config
668
+ }
669
+ };
670
+ if (variantPluginConfig._instructions) {
671
+ pluginConfig._instructions = [
672
+ ...pluginConfig._instructions ?? [],
673
+ ...variantPluginConfig._instructions.map(
674
+ (instruction) => `Sub-brand ${variant.id}: ${instruction}`
675
+ )
676
+ ];
677
+ }
678
+ }
679
+ }
680
+ if (Object.keys(subBrands).length > 0) {
681
+ pluginConfig.subBrands = subBrands;
682
+ }
683
+ if (pluginConfig._instructions) {
684
+ console.warn("\n=== IMPORTANT INSTRUCTIONS ===");
685
+ pluginConfig._instructions.forEach((inst) => console.warn(`- ${inst}`));
686
+ console.warn("==============================\n");
687
+ }
688
+ delete pluginConfig._instructions;
689
+ const outputPath = (0, import_node_path2.resolve)(parseOutputArg(args));
690
+ await fs2.writeFile(outputPath, JSON.stringify(pluginConfig, null, 2), "utf8");
691
+ console.log(`Successfully generated config and saved to ${outputPath}`);
692
+ }
693
+ function parseOutputArg(args) {
694
+ for (let i = 0; i < args.length; i += 1) {
695
+ const arg = args[i];
696
+ if (arg === "--output" || arg === "-o") {
697
+ const value = args[i + 1];
698
+ if (!value) {
699
+ throw new Error(`Missing value for ${arg}`);
700
+ }
701
+ return value;
702
+ }
703
+ }
704
+ return "rownd-plugin-config.json";
705
+ }
706
+ function parseRawOutputArg(args) {
707
+ for (let i = 0; i < args.length; i += 1) {
708
+ const arg = args[i];
709
+ if (arg === "--raw-output") {
710
+ const value = args[i + 1];
711
+ if (!value) {
712
+ throw new Error(`Missing value for ${arg}`);
713
+ }
714
+ return value;
715
+ }
716
+ }
717
+ return void 0;
718
+ }
719
+ function hasIncludeSubBrandsArg(args) {
720
+ return args.includes("--include-sub-brands");
721
+ }
722
+ if (require.main === module) {
723
+ run().catch((error) => {
724
+ console.error(error instanceof Error ? error.message : String(error));
725
+ process.exitCode = 1;
726
+ });
727
+ }
728
+ // Annotate the CommonJS export names for ESM import in node:
729
+ 0 && (module.exports = {
730
+ convertRowndConfigToPluginConfig
731
+ });