@ankhorage/studio 0.6.9 → 0.7.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.
Files changed (139) hide show
  1. package/README.md +1 -1
  2. package/dist/authSettings.d.ts +1 -0
  3. package/dist/authSettings.d.ts.map +1 -1
  4. package/dist/authSettings.js.map +1 -1
  5. package/dist/core/StudioProvider.d.ts +5 -2
  6. package/dist/core/StudioProvider.d.ts.map +1 -1
  7. package/dist/core/StudioProvider.js +212 -16
  8. package/dist/core/StudioProvider.js.map +1 -1
  9. package/dist/core/studioManifestDraftModel.d.ts +13 -0
  10. package/dist/core/studioManifestDraftModel.d.ts.map +1 -0
  11. package/dist/core/studioManifestDraftModel.js +34 -0
  12. package/dist/core/studioManifestDraftModel.js.map +1 -0
  13. package/dist/core/studioManifestPersistenceModel.d.ts +21 -0
  14. package/dist/core/studioManifestPersistenceModel.d.ts.map +1 -0
  15. package/dist/core/studioManifestPersistenceModel.js +46 -0
  16. package/dist/core/studioManifestPersistenceModel.js.map +1 -0
  17. package/dist/host/http/authRoutes.d.ts.map +1 -1
  18. package/dist/host/http/authRoutes.js +0 -31
  19. package/dist/host/http/authRoutes.js.map +1 -1
  20. package/dist/host/http/secretRoutes.d.ts.map +1 -1
  21. package/dist/host/http/secretRoutes.js +1 -24
  22. package/dist/host/http/secretRoutes.js.map +1 -1
  23. package/dist/host/index.d.ts +1 -2
  24. package/dist/host/index.d.ts.map +1 -1
  25. package/dist/host/index.js +1 -2
  26. package/dist/host/index.js.map +1 -1
  27. package/dist/host/layout/layoutGenerator.d.ts.map +1 -1
  28. package/dist/host/layout/layoutGenerator.js +55 -37
  29. package/dist/host/layout/layoutGenerator.js.map +1 -1
  30. package/dist/host/layout/templates/rootLayout.d.ts.map +1 -1
  31. package/dist/host/layout/templates/rootLayout.js +99 -15
  32. package/dist/host/layout/templates/rootLayout.js.map +1 -1
  33. package/dist/host/secrets/projectSecretService.d.ts +2 -12
  34. package/dist/host/secrets/projectSecretService.d.ts.map +1 -1
  35. package/dist/host/secrets/projectSecretService.js +1 -61
  36. package/dist/host/secrets/projectSecretService.js.map +1 -1
  37. package/dist/index.d.ts +13 -5
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +3 -1
  40. package/dist/index.js.map +1 -1
  41. package/dist/projectAuthApi.d.ts +0 -7
  42. package/dist/projectAuthApi.d.ts.map +1 -1
  43. package/dist/projectAuthApi.js +0 -34
  44. package/dist/projectAuthApi.js.map +1 -1
  45. package/dist/projectSecretApi.d.ts +1 -8
  46. package/dist/projectSecretApi.d.ts.map +1 -1
  47. package/dist/projectSecretApi.js +1 -8
  48. package/dist/projectSecretApi.js.map +1 -1
  49. package/dist/root.d.ts +2 -0
  50. package/dist/root.d.ts.map +1 -1
  51. package/dist/root.js +2 -0
  52. package/dist/root.js.map +1 -1
  53. package/dist/studioAdminRouteModel.d.ts +43 -9
  54. package/dist/studioAdminRouteModel.d.ts.map +1 -1
  55. package/dist/studioAdminRouteModel.js +176 -11
  56. package/dist/studioAdminRouteModel.js.map +1 -1
  57. package/dist/ui/admin/AnkhAdminPage.d.ts +7 -0
  58. package/dist/ui/admin/AnkhAdminPage.d.ts.map +1 -0
  59. package/dist/ui/admin/AnkhAdminPage.js +31 -0
  60. package/dist/ui/admin/AnkhAdminPage.js.map +1 -0
  61. package/dist/ui/admin/AnkhAdminShell.d.ts +6 -0
  62. package/dist/ui/admin/AnkhAdminShell.d.ts.map +1 -0
  63. package/dist/ui/admin/AnkhAdminShell.js +87 -0
  64. package/dist/ui/admin/AnkhAdminShell.js.map +1 -0
  65. package/dist/ui/admin/AuthAdminSession.d.ts +25 -0
  66. package/dist/ui/admin/AuthAdminSession.d.ts.map +1 -0
  67. package/dist/ui/admin/AuthAdminSession.js +96 -0
  68. package/dist/ui/admin/AuthAdminSession.js.map +1 -0
  69. package/dist/ui/admin/adminPagePrimitives.d.ts +34 -0
  70. package/dist/ui/admin/adminPagePrimitives.d.ts.map +1 -0
  71. package/dist/ui/admin/adminPagePrimitives.js +69 -0
  72. package/dist/ui/admin/adminPagePrimitives.js.map +1 -0
  73. package/dist/ui/admin/adminPageUtils.d.ts +4 -0
  74. package/dist/ui/admin/adminPageUtils.d.ts.map +1 -0
  75. package/dist/ui/admin/adminPageUtils.js +14 -0
  76. package/dist/ui/admin/adminPageUtils.js.map +1 -0
  77. package/dist/ui/admin/pages/ApisAdminPage.d.ts +7 -0
  78. package/dist/ui/admin/pages/ApisAdminPage.d.ts.map +1 -0
  79. package/dist/ui/admin/pages/ApisAdminPage.js +21 -0
  80. package/dist/ui/admin/pages/ApisAdminPage.js.map +1 -0
  81. package/dist/ui/admin/pages/AuthAdminPage.d.ts +10 -0
  82. package/dist/ui/admin/pages/AuthAdminPage.d.ts.map +1 -0
  83. package/dist/ui/admin/pages/AuthAdminPage.js +596 -0
  84. package/dist/ui/admin/pages/AuthAdminPage.js.map +1 -0
  85. package/dist/ui/admin/pages/OverviewAdminPage.d.ts +3 -0
  86. package/dist/ui/admin/pages/OverviewAdminPage.d.ts.map +1 -0
  87. package/dist/ui/admin/pages/OverviewAdminPage.js +16 -0
  88. package/dist/ui/admin/pages/OverviewAdminPage.js.map +1 -0
  89. package/dist/ui/admin/pages/PropertiesAdminPage.d.ts +5 -0
  90. package/dist/ui/admin/pages/PropertiesAdminPage.d.ts.map +1 -0
  91. package/dist/ui/admin/pages/PropertiesAdminPage.js +28 -0
  92. package/dist/ui/admin/pages/PropertiesAdminPage.js.map +1 -0
  93. package/dist/ui/admin/pages/SecretsAdminPage.d.ts +5 -0
  94. package/dist/ui/admin/pages/SecretsAdminPage.d.ts.map +1 -0
  95. package/dist/ui/{StudioAdminOverlay.js → admin/pages/SecretsAdminPage.js} +57 -44
  96. package/dist/ui/{StudioAdminOverlay.js.map → admin/pages/SecretsAdminPage.js.map} +1 -1
  97. package/dist/ui/admin/pages/ThemeAdminPage.d.ts +3 -0
  98. package/dist/ui/admin/pages/ThemeAdminPage.d.ts.map +1 -0
  99. package/dist/ui/admin/pages/ThemeAdminPage.js +51 -0
  100. package/dist/ui/admin/pages/ThemeAdminPage.js.map +1 -0
  101. package/dist/ui/admin/pages/adminAuthCredentialFlow.d.ts +32 -0
  102. package/dist/ui/admin/pages/adminAuthCredentialFlow.d.ts.map +1 -0
  103. package/dist/ui/admin/pages/adminAuthCredentialFlow.js +168 -0
  104. package/dist/ui/admin/pages/adminAuthCredentialFlow.js.map +1 -0
  105. package/dist/ui/admin/pages/adminAuthHealthFlow.d.ts +18 -0
  106. package/dist/ui/admin/pages/adminAuthHealthFlow.d.ts.map +1 -0
  107. package/dist/ui/admin/pages/adminAuthHealthFlow.js +24 -0
  108. package/dist/ui/admin/pages/adminAuthHealthFlow.js.map +1 -0
  109. package/dist/ui/admin/pages/adminAuthSessionModel.d.ts +67 -0
  110. package/dist/ui/admin/pages/adminAuthSessionModel.d.ts.map +1 -0
  111. package/dist/ui/admin/pages/adminAuthSessionModel.js +176 -0
  112. package/dist/ui/admin/pages/adminAuthSessionModel.js.map +1 -0
  113. package/dist/ui/admin/pages/adminDataSourceOperations.d.ts +10 -0
  114. package/dist/ui/admin/pages/adminDataSourceOperations.d.ts.map +1 -0
  115. package/dist/ui/admin/pages/adminDataSourceOperations.js +11 -0
  116. package/dist/ui/admin/pages/adminDataSourceOperations.js.map +1 -0
  117. package/dist/ui/admin/pages/adminThemeHarmony.d.ts +4 -0
  118. package/dist/ui/admin/pages/adminThemeHarmony.d.ts.map +1 -0
  119. package/dist/ui/admin/pages/adminThemeHarmony.js +12 -0
  120. package/dist/ui/admin/pages/adminThemeHarmony.js.map +1 -0
  121. package/dist/ui/admin/pages/adminThemeModel.d.ts +22 -0
  122. package/dist/ui/admin/pages/adminThemeModel.d.ts.map +1 -0
  123. package/dist/ui/admin/pages/adminThemeModel.js +22 -0
  124. package/dist/ui/admin/pages/adminThemeModel.js.map +1 -0
  125. package/dist/ui/useStudioAppBarAugmentation.d.ts +0 -1
  126. package/dist/ui/useStudioAppBarAugmentation.d.ts.map +1 -1
  127. package/dist/ui/useStudioAppBarAugmentation.js +20 -40
  128. package/dist/ui/useStudioAppBarAugmentation.js.map +1 -1
  129. package/package.json +2 -2
  130. package/dist/host/auth/projectAuthService.d.ts +0 -23
  131. package/dist/host/auth/projectAuthService.d.ts.map +0 -1
  132. package/dist/host/auth/projectAuthService.js +0 -63
  133. package/dist/host/auth/projectAuthService.js.map +0 -1
  134. package/dist/ui/StudioAdminOverlay.d.ts +0 -7
  135. package/dist/ui/StudioAdminOverlay.d.ts.map +0 -1
  136. package/dist/ui/StudioAuthSettingsOverlay.d.ts +0 -9
  137. package/dist/ui/StudioAuthSettingsOverlay.d.ts.map +0 -1
  138. package/dist/ui/StudioAuthSettingsOverlay.js +0 -607
  139. package/dist/ui/StudioAuthSettingsOverlay.js.map +0 -1
@@ -1,607 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { DEFAULT_AUTH_FLOW, } from '@ankhorage/contracts';
3
- import { getSupabaseOAuthProviderDefinition, SUPABASE_OAUTH_PROVIDER_IDS, } from '@ankhorage/supabase-auth';
4
- import { Heading, IconButton, Text, useZoraTheme } from '@ankhorage/zora';
5
- import { useRouter } from 'expo-router';
6
- import React, { useCallback, useEffect, useState } from 'react';
7
- import { ActivityIndicator, Pressable, ScrollView, StyleSheet, Switch, TextInput, View, } from 'react-native';
8
- import { SafeAreaView } from 'react-native-safe-area-context';
9
- import { readStudioAuthSettings } from '../authSettings';
10
- import { getProjectAuthHealth, getProjectAuthSettings, ProjectAuthApiError, saveProjectAuthSettings, } from '../projectAuthApi';
11
- import { configureProjectOAuthProvider } from '../projectSecretApi';
12
- const SIGN_IN_IDENTIFIERS = ['email', 'phone', 'username'];
13
- const PROFILE_FIELDS = [
14
- 'email',
15
- 'phone',
16
- 'username',
17
- 'firstName',
18
- 'lastName',
19
- 'displayName',
20
- 'avatarUrl',
21
- ];
22
- export function StudioAuthSettingsOverlay(props) {
23
- const { projectId, manifest, onClose } = props;
24
- const { theme } = useZoraTheme();
25
- const router = useRouter();
26
- const [draft, setDraft] = useState(() => readStudioAuthSettings(manifest ?? createFallbackManifest()) ?? createDefaultSettings());
27
- const [health, setHealth] = useState(null);
28
- const [loading, setLoading] = useState(true);
29
- const [saving, setSaving] = useState(false);
30
- const [message, setMessage] = useState(null);
31
- const [partialFailure, setPartialFailure] = useState(null);
32
- const reload = useCallback(async () => {
33
- setLoading(true);
34
- try {
35
- const [loaded, loadedHealth] = await Promise.all([
36
- getProjectAuthSettings(projectId),
37
- getProjectAuthHealth({ projectId, environment: 'local' }),
38
- ]);
39
- setDraft(loaded ?? createDefaultSettings());
40
- setHealth(loadedHealth);
41
- setPartialFailure(null);
42
- setMessage(null);
43
- }
44
- catch (error) {
45
- const local = manifest ? readStudioAuthSettings(manifest) : null;
46
- if (local)
47
- setDraft(local);
48
- setMessage(toMessage(error));
49
- }
50
- finally {
51
- setLoading(false);
52
- }
53
- }, [manifest, projectId]);
54
- useEffect(() => {
55
- void reload();
56
- }, [reload]);
57
- const refreshHealth = useCallback(async () => {
58
- try {
59
- setHealth(await getProjectAuthHealth({ projectId, environment: 'local' }));
60
- }
61
- catch (error) {
62
- setMessage(toMessage(error));
63
- }
64
- }, [projectId]);
65
- const save = useCallback(async () => {
66
- setSaving(true);
67
- setMessage(null);
68
- try {
69
- const saved = await saveProjectAuthSettings({ projectId, config: draft });
70
- setDraft(saved);
71
- setHealth(await getProjectAuthHealth({ projectId, environment: 'local' }));
72
- setPartialFailure(null);
73
- setMessage('Authentication configuration saved to the Studio manifest draft.');
74
- }
75
- catch (error) {
76
- setMessage(toMessage(error));
77
- }
78
- finally {
79
- setSaving(false);
80
- }
81
- }, [draft, projectId]);
82
- const retryPartialFailure = useCallback(async () => {
83
- if (!partialFailure)
84
- return;
85
- setSaving(true);
86
- setMessage(null);
87
- try {
88
- const persisted = (await getProjectAuthSettings(projectId)) ?? createDefaultSettings();
89
- const persistedOAuth = persisted.oauth ?? createDefaultOAuth();
90
- await saveProjectAuthSettings({
91
- projectId,
92
- config: {
93
- ...persisted,
94
- oauth: {
95
- ...persistedOAuth,
96
- providers: upsertProvider(persistedOAuth.providers, partialFailure.intendedProvider),
97
- },
98
- },
99
- });
100
- setDraft((current) => {
101
- const currentOAuth = current.oauth ?? createDefaultOAuth();
102
- return {
103
- ...current,
104
- oauth: {
105
- ...currentOAuth,
106
- providers: upsertProvider(currentOAuth.providers, partialFailure.intendedProvider),
107
- },
108
- };
109
- });
110
- await refreshHealth();
111
- setPartialFailure(null);
112
- setMessage(`Linked ${partialFailure.credentialsRef} to the provider configuration.`);
113
- }
114
- catch (error) {
115
- setMessage(toMessage(error));
116
- }
117
- finally {
118
- setSaving(false);
119
- }
120
- }, [partialFailure, projectId, refreshHealth]);
121
- const authEnabled = draft.scope !== 'none';
122
- const signUpEnabled = draft.signUp !== undefined;
123
- const profileEnabled = draft.profile !== undefined;
124
- const oauth = draft.oauth ?? createDefaultOAuth();
125
- return (_jsxs(SafeAreaView, { style: [
126
- styles.overlay,
127
- { backgroundColor: theme.colors.background, borderColor: theme.colors.border },
128
- ], children: [_jsxs(View, { style: [styles.header, { borderBottomColor: theme.colors.border }], children: [_jsxs(View, { style: styles.grow, children: [_jsx(Heading, { level: 2, text: "Authentication" }), _jsx(Text, { color: "neutral", emphasis: "muted", variant: "bodySmall", children: "Configure canonical auth methods, routes, profile settings, and provider activation." })] }), _jsx(IconButton, { icon: { name: 'close-outline' }, label: "Close authentication settings", color: "neutral", variant: "ghost", onPress: onClose })] }), _jsxs(ScrollView, { contentContainerStyle: styles.content, keyboardShouldPersistTaps: "handled", children: [loading ? _jsx(ActivityIndicator, {}) : null, _jsx(AuthHealthCard, { health: health }), _jsxs(Card, { title: "Overview", children: [_jsx(SwitchSetting, { title: "Authentication enabled", description: "Disabling auth keeps the configuration but sets the canonical scope to none.", value: authEnabled, onValueChange: (enabled) => setDraft((current) => ({ ...current, scope: enabled ? 'global' : 'none' })) }), _jsx(KeyValue, { label: "Provider", value: "Supabase" }), _jsx(KeyValue, { label: "Configuration source", value: "infra.auth" }), _jsx(Text, { color: "neutral", emphasis: "muted", variant: "caption", children: "Roles, RBAC, ABAC, registered users, and user passwords are intentionally not managed here." })] }), _jsxs(Card, { title: "Email and password", children: [_jsx(Text, { weight: "semiBold", children: "Sign-in identifiers" }), _jsx(View, { style: styles.choiceRow, children: SIGN_IN_IDENTIFIERS.map((identifier) => {
129
- const selected = draft.signIn.identifiers.includes(identifier);
130
- return (_jsx(Choice, { label: identifier, selected: selected, onPress: () => setDraft((current) => {
131
- const identifiers = selected
132
- ? current.signIn.identifiers.filter((value) => value !== identifier)
133
- : [...current.signIn.identifiers, identifier];
134
- if (identifiers.length === 0)
135
- return current;
136
- return { ...current, signIn: { identifiers } };
137
- }) }, identifier));
138
- }) }), _jsx(SwitchSetting, { title: "Public sign-up enabled", description: "When disabled, sign-up configuration is removed from the desired state.", value: signUpEnabled, onValueChange: (enabled) => setDraft((current) => enabled
139
- ? {
140
- ...current,
141
- signUp: current.signUp ?? {
142
- requiredFields: ['email', 'password'],
143
- optionalFields: [],
144
- signUpPolicy: 'requireVerification',
145
- },
146
- }
147
- : omitSignUp(current)) }), draft.signUp ? (_jsxs(_Fragment, { children: [_jsx(Field, { label: "Required sign-up fields (comma-separated)", children: _jsx(Input, { value: draft.signUp.requiredFields.join(', '), autoCapitalize: "none", onChangeText: (value) => setDraft((current) => current.signUp
148
- ? {
149
- ...current,
150
- signUp: {
151
- ...current.signUp,
152
- requiredFields: splitList(value),
153
- },
154
- }
155
- : current) }) }), _jsx(Field, { label: "Optional sign-up fields (comma-separated)", children: _jsx(Input, { value: (draft.signUp.optionalFields ?? []).join(', '), autoCapitalize: "none", onChangeText: (value) => setDraft((current) => current.signUp
156
- ? {
157
- ...current,
158
- signUp: {
159
- ...current.signUp,
160
- optionalFields: splitList(value),
161
- },
162
- }
163
- : current) }) }), _jsx(SwitchSetting, { title: "Email confirmation required", description: "Uses the canonical requireVerification sign-up policy.", value: draft.signUp.signUpPolicy === 'requireVerification', onValueChange: (required) => setDraft((current) => current.signUp
164
- ? {
165
- ...current,
166
- signUp: {
167
- ...current.signUp,
168
- signUpPolicy: required ? 'requireVerification' : 'autoSignIn',
169
- },
170
- }
171
- : current) })] })) : null, _jsx(Field, { label: "Forgot-password route", children: _jsx(Input, { value: draft.flow.forgotPasswordRoute ?? '', autoCapitalize: "none", onChangeText: (forgotPasswordRoute) => setDraft((current) => ({
172
- ...current,
173
- flow: {
174
- ...current.flow,
175
- ...(forgotPasswordRoute.trim() ? { forgotPasswordRoute } : {}),
176
- },
177
- })) }) })] }), _jsxs(Card, { title: "Routes", children: [_jsx(RouteField, { label: "Sign-in route", value: draft.flow.signInRoute, onChange: (signInRoute) => updateFlow(setDraft, { signInRoute }) }), _jsx(RouteField, { label: "Sign-up route", value: draft.flow.signUpRoute ?? '', onChange: (signUpRoute) => updateFlow(setDraft, { signUpRoute }) }), _jsx(RouteField, { label: "Sign-out route", value: draft.flow.signOutRoute ?? '', onChange: (signOutRoute) => updateFlow(setDraft, { signOutRoute }) }), _jsx(RouteField, { label: "Post-sign-in route", value: draft.flow.postSignInRoute, onChange: (postSignInRoute) => updateFlow(setDraft, { postSignInRoute }) }), _jsx(RouteField, { label: "Unauthorized route", value: draft.flow.unauthorizedRoute ?? '', onChange: (unauthorizedRoute) => updateFlow(setDraft, { unauthorizedRoute }) })] }), _jsxs(Card, { title: "OAuth providers", children: [_jsx(SwitchSetting, { title: "OAuth enabled", description: "Provider credentials remain in the server-side secret store.", value: oauth.enabled, onValueChange: (enabled) => setDraft((current) => ({
178
- ...current,
179
- oauth: { ...(current.oauth ?? createDefaultOAuth()), enabled },
180
- })) }), _jsx(Field, { label: "Callback route", children: _jsx(Input, { value: oauth.callbackRoute, autoCapitalize: "none", onChangeText: (callbackRoute) => setDraft((current) => ({
181
- ...current,
182
- oauth: { ...(current.oauth ?? createDefaultOAuth()), callbackRoute },
183
- })) }) }), SUPABASE_OAUTH_PROVIDER_IDS.map((providerId) => (_jsx(OAuthProviderSetting, { projectId: projectId, providerId: providerId, oauth: oauth, providerHealth: health?.providers.find((provider) => provider.providerId === providerId), onChange: (nextOAuth, nextMessage) => {
184
- setDraft((current) => ({ ...current, oauth: nextOAuth }));
185
- setMessage(nextMessage);
186
- }, onSaved: (nextOAuth, nextMessage) => {
187
- setDraft((current) => ({ ...current, oauth: nextOAuth }));
188
- setMessage(nextMessage);
189
- setPartialFailure(null);
190
- void refreshHealth();
191
- }, onPartialFailure: (failure) => {
192
- setPartialFailure(failure);
193
- setMessage(`${failure.credentialsRef} was saved, but the manifest link failed. Retry the link or open project secrets for cleanup.`);
194
- } }, providerId))), partialFailure ? (_jsxs(View, { style: styles.partialFailure, children: [_jsx(Text, { color: "warning", weight: "semiBold", children: "Credential secret saved, manifest link failed." }), _jsxs(Text, { color: "neutral", emphasis: "muted", variant: "caption", children: ["Ref: ", partialFailure.credentialsRef] }), _jsxs(Text, { color: "neutral", emphasis: "muted", variant: "caption", children: ["Configured fields: ", partialFailure.configuredFields.join(', ') || 'none'] }), _jsxs(View, { style: styles.actions, children: [_jsx(SecondaryButton, { label: "Retry manifest link", onPress: () => void retryPartialFailure() }), _jsx(SecondaryButton, { label: "Open cleanup", onPress: () => {
195
- router.push('/ankh/secrets');
196
- } })] })] })) : null, _jsx(View, { style: styles.actions, children: _jsx(SecondaryButton, { label: "Open project secrets", onPress: () => {
197
- router.push('/ankh/secrets');
198
- } }) })] }), _jsxs(Card, { title: "Profile", children: [_jsx(SwitchSetting, { title: "Profile table enabled", description: "Profiles are separate from Supabase Auth users. The users table is not an option.", value: profileEnabled, onValueChange: (enabled) => setDraft((current) => enabled
199
- ? {
200
- ...current,
201
- profile: current.profile ?? {
202
- table: 'profiles',
203
- fields: ['email', 'displayName', 'avatarUrl'],
204
- primaryKey: 'authUserId',
205
- createStrategy: 'trigger',
206
- updateStrategy: 'api',
207
- },
208
- }
209
- : omitProfile(current)) }), draft.profile ? (_jsxs(_Fragment, { children: [_jsx(Field, { label: "Profile table", children: _jsx(Input, { value: draft.profile.table ?? 'profiles', autoCapitalize: "none", onChangeText: (table) => setDraft((current) => current.profile
210
- ? { ...current, profile: { ...current.profile, table } }
211
- : current) }) }), _jsx(Text, { weight: "semiBold", children: "Profile fields" }), _jsx(View, { style: styles.choiceRow, children: PROFILE_FIELDS.map((field) => {
212
- const selected = draft.profile?.fields.includes(field) ?? false;
213
- return (_jsx(Choice, { label: field, selected: selected, onPress: () => setDraft((current) => {
214
- if (!current.profile)
215
- return current;
216
- const fields = selected
217
- ? current.profile.fields.filter((value) => value !== field)
218
- : [...current.profile.fields, field];
219
- return {
220
- ...current,
221
- profile: { ...current.profile, fields },
222
- };
223
- }) }, field));
224
- }) }), _jsx(KeyValue, { label: "Primary key", value: "authUserId" }), _jsx(Field, { label: "Create strategy", children: _jsx(View, { style: styles.choiceRow, children: ['trigger', 'api', 'app'].map((strategy) => (_jsx(Choice, { label: strategy, selected: draft.profile?.createStrategy === strategy, onPress: () => setDraft((current) => current.profile
225
- ? {
226
- ...current,
227
- profile: { ...current.profile, createStrategy: strategy },
228
- }
229
- : current) }, strategy))) }) }), _jsx(Field, { label: "Update strategy", children: _jsx(View, { style: styles.choiceRow, children: ['api', 'app'].map((strategy) => (_jsx(Choice, { label: strategy, selected: draft.profile?.updateStrategy === strategy, onPress: () => setDraft((current) => current.profile
230
- ? {
231
- ...current,
232
- profile: { ...current.profile, updateStrategy: strategy },
233
- }
234
- : current) }, strategy))) }) })] })) : null] }), _jsxs(View, { style: styles.footerActions, children: [_jsx(SecondaryButton, { label: "Reload", onPress: () => void reload() }), _jsx(PrimaryButton, { label: "Save authentication", loading: saving, onPress: () => void save() })] }), message ? _jsx(Message, { text: message }) : null] })] }));
235
- }
236
- function OAuthProviderSetting(props) {
237
- const definition = getSupabaseOAuthProviderDefinition(props.providerId);
238
- const current = props.oauth.providers.find((provider) => provider.id === props.providerId);
239
- const requiredFields = props.providerHealth?.requiredFields ??
240
- definition?.secretFields.map((field) => field.name) ??
241
- [];
242
- const configuredFields = props.providerHealth?.configuredFields ?? [];
243
- const credentialsComplete = definition !== null &&
244
- Boolean(current?.credentialsRef) &&
245
- requiredFields.length > 0 &&
246
- requiredFields.every((field) => configuredFields.includes(field));
247
- const enabled = current?.enabled === true;
248
- const [credentialValues, setCredentialValues] = useState({});
249
- const [savingCredentials, setSavingCredentials] = useState(false);
250
- const [credentialMessage, setCredentialMessage] = useState(null);
251
- const setEnabled = (nextEnabled) => {
252
- if (nextEnabled && !credentialsComplete) {
253
- props.onChange(props.oauth, `Complete ${definition?.label ?? props.providerId} credentials before enabling the provider.`);
254
- return;
255
- }
256
- const nextProvider = {
257
- ...(current ?? {
258
- id: props.providerId,
259
- label: definition?.label ?? props.providerId,
260
- scopes: [...(definition?.defaultScopes ?? [])],
261
- }),
262
- enabled: nextEnabled,
263
- };
264
- props.onChange({
265
- ...props.oauth,
266
- providers: upsertProvider(props.oauth.providers, nextProvider),
267
- }, null);
268
- };
269
- const saveCredentials = async () => {
270
- if (!definition) {
271
- setCredentialMessage('The selected OAuth provider is not supported.');
272
- return;
273
- }
274
- const entries = definition.secretFields.map((field) => [field.name, credentialValues[field.name] ?? '']);
275
- if (entries.some(([, value]) => !value)) {
276
- setCredentialMessage('Enter a complete credential payload. Existing values cannot be merged.');
277
- return;
278
- }
279
- const credentialsRef = current?.credentialsRef ?? `auth/oauth/${props.providerId}`;
280
- const intendedProvider = {
281
- ...(current ?? {
282
- id: props.providerId,
283
- label: definition.label,
284
- scopes: [...definition.defaultScopes],
285
- }),
286
- enabled,
287
- credentialsRef,
288
- };
289
- const intendedOAuth = {
290
- ...props.oauth,
291
- providers: upsertProvider(props.oauth.providers, intendedProvider),
292
- };
293
- setSavingCredentials(true);
294
- setCredentialMessage(null);
295
- try {
296
- const result = await configureProjectOAuthProvider({
297
- projectId: props.projectId,
298
- providerId: props.providerId,
299
- environment: 'local',
300
- credentialsRef,
301
- enabled,
302
- label: current?.label ?? definition.label,
303
- scopes: current?.scopes ?? definition.defaultScopes,
304
- payload: Object.freeze(Object.fromEntries(entries)),
305
- });
306
- if (result.ok) {
307
- props.onSaved(mergeOAuthProviderCredentialsRef(intendedOAuth, intendedProvider, result.credentialsRef), `${definition.label} credentials saved through ${result.credentialsRef}.`);
308
- return;
309
- }
310
- if (result.state === 'secret_saved_manifest_failed' &&
311
- result.credentialsRef &&
312
- result.metadata) {
313
- props.onPartialFailure({
314
- state: 'secret_saved_manifest_failed',
315
- providerId: props.providerId,
316
- credentialsRef: result.credentialsRef,
317
- configuredFields: result.metadata.configuredFields,
318
- intendedProvider: {
319
- ...intendedProvider,
320
- credentialsRef: result.credentialsRef,
321
- },
322
- });
323
- return;
324
- }
325
- setCredentialMessage(result.error.message);
326
- }
327
- catch (error) {
328
- setCredentialMessage(toMessage(error));
329
- }
330
- finally {
331
- setCredentialValues({});
332
- setSavingCredentials(false);
333
- }
334
- };
335
- return (_jsxs(View, { style: styles.providerPanel, children: [_jsxs(View, { style: styles.providerRow, children: [_jsxs(View, { style: styles.grow, children: [_jsx(Text, { weight: "semiBold", children: definition?.label ?? props.providerId }), _jsxs(Text, { color: credentialsComplete ? 'success' : 'warning', emphasis: "muted", variant: "caption", children: [formatProviderHealthStatus(props.providerHealth?.status ?? 'missing'), current?.credentialsRef ? `: ${current.credentialsRef}` : ''] }), _jsxs(Text, { color: "neutral", emphasis: "muted", variant: "caption", children: ["Required fields: ", props.providerHealth?.requiredFields.join(', ') || 'loading'] })] }), _jsx(Switch, { value: enabled, onValueChange: setEnabled })] }), definition?.secretFields.map((field) => (_jsx(Field, { label: field.label, children: _jsx(Input, { value: credentialValues[field.name] ?? '', secureTextEntry: field.secret, autoCapitalize: "none", placeholder: credentialsComplete ? 'Enter complete replacement value' : field.label, onChangeText: (value) => setCredentialValues((currentValues) => ({
336
- ...currentValues,
337
- [field.name]: value,
338
- })) }) }, field.name))), _jsx(View, { style: styles.actions, children: _jsx(PrimaryButton, { label: credentialsComplete ? 'Replace credentials' : 'Save credentials', loading: savingCredentials, onPress: () => void saveCredentials() }) }), credentialMessage ? _jsx(Message, { text: credentialMessage }) : null] }));
339
- }
340
- function mergeOAuthProviderCredentialsRef(oauth, provider, credentialsRef) {
341
- return {
342
- ...oauth,
343
- providers: upsertProvider(oauth.providers, {
344
- ...provider,
345
- credentialsRef,
346
- }),
347
- };
348
- }
349
- function upsertProvider(providers, provider) {
350
- const index = providers.findIndex((candidate) => candidate.id === provider.id);
351
- if (index < 0)
352
- return [...providers, provider];
353
- return providers.map((candidate, candidateIndex) => candidateIndex === index ? provider : candidate);
354
- }
355
- function updateFlow(setDraft, patch) {
356
- setDraft((current) => ({ ...current, flow: { ...current.flow, ...patch } }));
357
- }
358
- function omitSignUp(settings) {
359
- const { signUp: _signUp, ...rest } = settings;
360
- return rest;
361
- }
362
- function omitProfile(settings) {
363
- const { profile: _profile, ...rest } = settings;
364
- return rest;
365
- }
366
- function createDefaultSettings() {
367
- return {
368
- scope: 'none',
369
- provider: 'supabase',
370
- flow: { ...DEFAULT_AUTH_FLOW },
371
- signIn: { identifiers: ['email'] },
372
- oauth: createDefaultOAuth(),
373
- };
374
- }
375
- function createDefaultOAuth() {
376
- return {
377
- enabled: false,
378
- callbackRoute: '/auth/callback',
379
- providers: [],
380
- };
381
- }
382
- function createFallbackManifest() {
383
- return {
384
- metadata: { name: 'App', slug: 'app', version: '1.0.0', themeId: 'default' },
385
- settings: { localization: { defaultLocale: 'en', locales: ['en'] } },
386
- infra: { plugins: [] },
387
- navigator: { type: 'stack', routes: [] },
388
- screens: {},
389
- themes: [],
390
- activeThemeId: 'default',
391
- };
392
- }
393
- function splitList(value) {
394
- return [
395
- ...new Set(value
396
- .split(',')
397
- .map((entry) => entry.trim())
398
- .filter(Boolean)),
399
- ];
400
- }
401
- function toMessage(error) {
402
- if (error instanceof ProjectAuthApiError)
403
- return error.message;
404
- return error instanceof Error ? error.message : 'Authentication configuration request failed.';
405
- }
406
- function AuthHealthCard({ health }) {
407
- if (!health) {
408
- return (_jsx(Card, { title: "Health", children: _jsx(Text, { color: "neutral", emphasis: "muted", variant: "bodySmall", children: "Auth health is unavailable." }) }));
409
- }
410
- return (_jsxs(Card, { title: "Health", children: [_jsx(Text, { color: healthStatusColor(health.status), weight: "semiBold", children: formatHealthStatus(health.status) }), _jsx(KeyValue, { label: "Callback route", value: health.callbackUrls.appCallbackRoute }), health.callbackUrls.providerRedirectUrl ? (_jsx(KeyValue, { label: "Provider redirect URL", value: health.callbackUrls.providerRedirectUrl })) : null, health.providers.map((provider) => (_jsxs(View, { style: styles.healthProvider, children: [_jsxs(Text, { weight: "semiBold", children: [provider.label, ": ", formatProviderHealthStatus(provider.status)] }), _jsxs(Text, { color: "neutral", emphasis: "muted", variant: "caption", children: ["Ref: ", provider.credentialsRef ?? 'none'] }), _jsxs(Text, { color: "neutral", emphasis: "muted", variant: "caption", children: ["Required fields: ", provider.requiredFields.join(', ') || 'none'] }), _jsxs(Text, { color: "neutral", emphasis: "muted", variant: "caption", children: ["Configured fields: ", provider.configuredFields.join(', ') || 'none'] }), provider.missingFields.length > 0 ? (_jsxs(Text, { color: "warning", variant: "caption", children: ["Missing fields: ", provider.missingFields.join(', ')] })) : null] }, provider.providerId))), health.diagnostics.map((diagnostic) => (_jsxs(View, { style: styles.diagnostic, children: [_jsx(Text, { color: diagnosticSeverityColor(diagnostic.severity), variant: "bodySmall", children: diagnostic.code }), _jsx(Text, { color: "neutral", emphasis: "muted", variant: "caption", children: diagnostic.message })] }, `${diagnostic.code}:${diagnostic.path ?? ''}`))), health.diagnostics.length === 0 ? (_jsx(Text, { color: "success", variant: "bodySmall", children: "No auth diagnostics." })) : null] }));
411
- }
412
- function formatHealthStatus(status) {
413
- if (status === 'healthy')
414
- return 'Healthy';
415
- if (status === 'warning')
416
- return 'Warning';
417
- if (status === 'error')
418
- return 'Error';
419
- return 'Not configured';
420
- }
421
- function formatProviderHealthStatus(status) {
422
- if (status === 'configured')
423
- return 'Configured';
424
- if (status === 'incomplete')
425
- return 'Incomplete';
426
- if (status === 'missing')
427
- return 'Missing secret';
428
- if (status === 'invalid')
429
- return 'Invalid';
430
- return 'Disabled';
431
- }
432
- function healthStatusColor(status) {
433
- if (status === 'healthy')
434
- return 'success';
435
- if (status === 'warning')
436
- return 'warning';
437
- if (status === 'error')
438
- return 'danger';
439
- return 'neutral';
440
- }
441
- function diagnosticSeverityColor(severity) {
442
- if (severity === 'error')
443
- return 'danger';
444
- if (severity === 'warning')
445
- return 'warning';
446
- return 'neutral';
447
- }
448
- function Card(props) {
449
- const { theme } = useZoraTheme();
450
- return (_jsxs(View, { style: [
451
- styles.card,
452
- { backgroundColor: theme.colors.surface, borderColor: theme.colors.border },
453
- ], children: [_jsx(Heading, { level: 3, text: props.title }), props.children] }));
454
- }
455
- function Field(props) {
456
- return (_jsxs(View, { style: styles.field, children: [_jsx(Text, { variant: "bodySmall", weight: "semiBold", children: props.label }), props.children] }));
457
- }
458
- function RouteField(props) {
459
- return (_jsx(Field, { label: props.label, children: _jsx(Input, { value: props.value, autoCapitalize: "none", onChangeText: props.onChange }) }));
460
- }
461
- function Input(props) {
462
- const { theme } = useZoraTheme();
463
- return (_jsx(TextInput, { ...props, placeholderTextColor: theme.colors.textMuted, style: [
464
- styles.input,
465
- {
466
- color: theme.colors.text,
467
- backgroundColor: theme.colors.background,
468
- borderColor: theme.colors.border,
469
- },
470
- props.style,
471
- ] }));
472
- }
473
- function SwitchSetting(props) {
474
- return (_jsxs(View, { style: styles.switchRow, children: [_jsxs(View, { style: styles.grow, children: [_jsx(Text, { weight: "semiBold", children: props.title }), _jsx(Text, { color: "neutral", emphasis: "muted", variant: "caption", children: props.description })] }), _jsx(Switch, { value: props.value, onValueChange: props.onValueChange })] }));
475
- }
476
- function Choice(props) {
477
- const { theme } = useZoraTheme();
478
- return (_jsx(Pressable, { onPress: props.onPress, style: [
479
- styles.choice,
480
- {
481
- borderColor: props.selected ? theme.colors.primary : theme.colors.border,
482
- backgroundColor: props.selected ? theme.colors.surface : theme.colors.background,
483
- },
484
- ], children: _jsx(Text, { color: props.selected ? 'primary' : 'neutral', variant: "bodySmall", weight: "semiBold", children: props.label }) }));
485
- }
486
- function KeyValue(props) {
487
- return (_jsxs(View, { style: styles.keyValue, children: [_jsx(Text, { color: "neutral", emphasis: "muted", variant: "bodySmall", children: props.label }), _jsx(Text, { weight: "semiBold", children: props.value })] }));
488
- }
489
- function PrimaryButton(props) {
490
- const { theme } = useZoraTheme();
491
- return (_jsx(Pressable, { disabled: props.loading, onPress: props.onPress, style: [styles.primaryButton, { backgroundColor: theme.colors.primary }], children: props.loading ? (_jsx(ActivityIndicator, { color: "#ffffff" })) : (_jsx(Text, { emphasis: "inverse", weight: "semiBold", children: props.label })) }));
492
- }
493
- function SecondaryButton(props) {
494
- const { theme } = useZoraTheme();
495
- return (_jsx(Pressable, { onPress: props.onPress, style: [styles.secondaryButton, { borderColor: theme.colors.border }], children: _jsx(Text, { weight: "semiBold", children: props.label }) }));
496
- }
497
- function Message({ text }) {
498
- return (_jsx(Text, { color: "info", variant: "bodySmall", children: text }));
499
- }
500
- const styles = StyleSheet.create({
501
- overlay: {
502
- ...StyleSheet.absoluteFillObject,
503
- zIndex: 1000,
504
- borderWidth: 1,
505
- },
506
- header: {
507
- minHeight: 72,
508
- paddingHorizontal: 20,
509
- paddingVertical: 12,
510
- borderBottomWidth: 1,
511
- flexDirection: 'row',
512
- alignItems: 'center',
513
- gap: 16,
514
- },
515
- content: {
516
- width: '100%',
517
- maxWidth: 920,
518
- alignSelf: 'center',
519
- padding: 20,
520
- gap: 16,
521
- },
522
- card: {
523
- borderWidth: 1,
524
- borderRadius: 14,
525
- padding: 16,
526
- gap: 14,
527
- },
528
- grow: { flex: 1 },
529
- field: { gap: 6 },
530
- input: {
531
- minHeight: 42,
532
- borderWidth: 1,
533
- borderRadius: 8,
534
- paddingHorizontal: 12,
535
- paddingVertical: 8,
536
- },
537
- switchRow: {
538
- flexDirection: 'row',
539
- alignItems: 'center',
540
- gap: 16,
541
- },
542
- choiceRow: {
543
- flexDirection: 'row',
544
- flexWrap: 'wrap',
545
- gap: 8,
546
- },
547
- choice: {
548
- borderWidth: 1,
549
- borderRadius: 999,
550
- paddingHorizontal: 12,
551
- paddingVertical: 8,
552
- },
553
- providerRow: {
554
- flexDirection: 'row',
555
- alignItems: 'center',
556
- gap: 16,
557
- paddingVertical: 8,
558
- },
559
- providerPanel: {
560
- gap: 10,
561
- paddingVertical: 8,
562
- },
563
- partialFailure: {
564
- gap: 8,
565
- paddingVertical: 10,
566
- },
567
- healthProvider: {
568
- gap: 3,
569
- paddingVertical: 8,
570
- },
571
- diagnostic: {
572
- gap: 3,
573
- paddingVertical: 6,
574
- },
575
- keyValue: {
576
- flexDirection: 'row',
577
- alignItems: 'center',
578
- justifyContent: 'space-between',
579
- gap: 16,
580
- },
581
- actions: {
582
- flexDirection: 'row',
583
- justifyContent: 'flex-end',
584
- gap: 10,
585
- },
586
- footerActions: {
587
- flexDirection: 'row',
588
- justifyContent: 'flex-end',
589
- gap: 10,
590
- },
591
- primaryButton: {
592
- minHeight: 42,
593
- borderRadius: 8,
594
- paddingHorizontal: 16,
595
- alignItems: 'center',
596
- justifyContent: 'center',
597
- },
598
- secondaryButton: {
599
- minHeight: 42,
600
- borderWidth: 1,
601
- borderRadius: 8,
602
- paddingHorizontal: 16,
603
- alignItems: 'center',
604
- justifyContent: 'center',
605
- },
606
- });
607
- //# sourceMappingURL=StudioAuthSettingsOverlay.js.map