@supertokens-plugins/rownd-nodejs 0.2.0 → 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.
- package/README.md +20 -0
- package/dist/bulkMigrate.js +1096 -0
- package/dist/cli.js +54 -0
- package/dist/generateAppConfig.js +731 -0
- package/dist/index.d.mts +826 -0
- package/dist/index.d.ts +826 -0
- package/dist/index.js +2752 -0
- package/dist/index.mjs +2732 -0
- package/dist/initConfig.js +136 -0
- package/dist/setupCoreInstance.js +295 -0
- package/package.json +13 -10
- package/scripts/bulkMigrate.ts +0 -450
- package/scripts/config.yaml +0 -27
- package/src/constants.ts +0 -5
- package/src/errors.ts +0 -13
- package/src/index.ts +0 -7
- package/src/logger.ts +0 -7
- package/src/plugin.test.ts +0 -790
- package/src/plugin.ts +0 -189
- package/src/pluginImplementation.ts +0 -187
- package/src/telemetry/axiomTelemetryClient.ts +0 -34
- package/src/telemetry/createTelemetryClient.ts +0 -87
- package/src/telemetry/openTelemetryClient.ts +0 -32
- package/src/types.ts +0 -124
|
@@ -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
|
+
});
|