@hexclave/tanstack-start 1.0.24 → 1.0.26
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/dist/components/elements/sidebar-layout.js +1 -1
- package/dist/components-page/hexclave-handler-client.js +6 -5
- package/dist/components-page/hexclave-handler-client.js.map +1 -1
- package/dist/dev-tool/dev-tool-core.d.ts.map +1 -1
- package/dist/dev-tool/dev-tool-core.js +4 -68
- package/dist/dev-tool/dev-tool-core.js.map +1 -1
- package/dist/esm/components/elements/sidebar-layout.js +1 -1
- package/dist/esm/components-page/hexclave-handler-client.js +6 -5
- package/dist/esm/components-page/hexclave-handler-client.js.map +1 -1
- package/dist/esm/dev-tool/dev-tool-core.d.ts.map +1 -1
- package/dist/esm/dev-tool/dev-tool-core.js +4 -68
- package/dist/esm/dev-tool/dev-tool-core.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +7 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.js +35 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js +18 -11
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/common.js +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/event-tracker.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/event-tracker.js +2 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/event-tracker.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/event-tracker.test.js +26 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/event-tracker.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.d.ts +7 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.js +11 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.test.js +43 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/admin-app.d.ts +2 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/admin-app.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/admin-app.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.d.ts +1 -0
- package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/esm/lib/hexclave-app/index.d.ts +2 -1
- package/dist/esm/lib/hexclave-app/plan-usage/index.d.ts +27 -0
- package/dist/esm/lib/hexclave-app/plan-usage/index.d.ts.map +1 -0
- package/dist/esm/lib/hexclave-app/plan-usage/index.js +1 -0
- package/dist/esm/lib/hexclave-app/projects/index.d.ts +7 -0
- package/dist/esm/lib/hexclave-app/projects/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/projects/index.js.map +1 -1
- package/dist/esm/pushed-config-error-overlay/index.d.ts +7 -0
- package/dist/esm/pushed-config-error-overlay/index.d.ts.map +1 -0
- package/dist/esm/pushed-config-error-overlay/index.js +464 -0
- package/dist/esm/pushed-config-error-overlay/index.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +7 -0
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.js +35 -1
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +1 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js +18 -11
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/common.js +1 -1
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.js +1 -0
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.test.js +26 -0
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/session-replay.d.ts +7 -1
- package/dist/lib/hexclave-app/apps/implementations/session-replay.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/session-replay.js +11 -0
- package/dist/lib/hexclave-app/apps/implementations/session-replay.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/session-replay.test.js +43 -0
- package/dist/lib/hexclave-app/apps/implementations/session-replay.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/admin-app.d.ts +2 -1
- package/dist/lib/hexclave-app/apps/interfaces/admin-app.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/admin-app.js.map +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/client-app.d.ts +1 -0
- package/dist/lib/hexclave-app/apps/interfaces/client-app.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/lib/hexclave-app/index.d.ts +2 -1
- package/dist/lib/hexclave-app/plan-usage/index.d.ts +27 -0
- package/dist/lib/hexclave-app/plan-usage/index.d.ts.map +1 -0
- package/dist/lib/hexclave-app/plan-usage/index.js +0 -0
- package/dist/lib/hexclave-app/projects/index.d.ts +7 -0
- package/dist/lib/hexclave-app/projects/index.d.ts.map +1 -1
- package/dist/lib/hexclave-app/projects/index.js.map +1 -1
- package/dist/pushed-config-error-overlay/index.d.ts +7 -0
- package/dist/pushed-config-error-overlay/index.d.ts.map +1 -0
- package/dist/pushed-config-error-overlay/index.js +466 -0
- package/dist/pushed-config-error-overlay/index.js.map +1 -0
- package/package.json +3 -3
- package/src/components-page/hexclave-handler-client.tsx +6 -5
- package/src/dev-tool/dev-tool-core.ts +5 -59
- package/src/lib/hexclave-app/apps/implementations/admin-app-impl.ts +44 -1
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +20 -11
- package/src/lib/hexclave-app/apps/implementations/event-tracker.test.ts +33 -0
- package/src/lib/hexclave-app/apps/implementations/event-tracker.ts +6 -1
- package/src/lib/hexclave-app/apps/implementations/session-replay.test.ts +52 -0
- package/src/lib/hexclave-app/apps/implementations/session-replay.ts +20 -0
- package/src/lib/hexclave-app/apps/interfaces/admin-app.ts +2 -0
- package/src/lib/hexclave-app/apps/interfaces/client-app.ts +1 -0
- package/src/lib/hexclave-app/index.ts +8 -0
- package/src/lib/hexclave-app/plan-usage/index.ts +29 -0
- package/src/lib/hexclave-app/projects/index.ts +3 -0
- package/src/pushed-config-error-overlay/index.ts +548 -0
|
@@ -638,11 +638,6 @@ function createOverviewTab(app: StackClientApp<true>): TabResult {
|
|
|
638
638
|
|
|
639
639
|
const actions = h('div', { className: 'sdt-ov-actions' });
|
|
640
640
|
const toast = h('div', { className: 'sdt-ov-toast', style: { display: 'none' } });
|
|
641
|
-
const emailRow = h('div', { className: 'sdt-ov-email-input' });
|
|
642
|
-
const emailInput = h('input', { type: 'email', placeholder: 'Sign in as email\u2026' }) as HTMLInputElement;
|
|
643
|
-
const emailBtn = h('button', null);
|
|
644
|
-
setHtml(emailBtn, '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>');
|
|
645
|
-
emailRow.append(emailInput, emailBtn);
|
|
646
641
|
|
|
647
642
|
function isBestEffortOverviewError(error: unknown) {
|
|
648
643
|
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
@@ -704,15 +699,14 @@ function createOverviewTab(app: StackClientApp<true>): TabResult {
|
|
|
704
699
|
});
|
|
705
700
|
actions.append(signOutBtn, randomBtn);
|
|
706
701
|
} else {
|
|
707
|
-
const quickBtn = h('button', { className: 'sdt-ov-btn sdt-ov-btn-primary sdt-ov-btn-wide' }, loading ? 'Working\u2026' : 'Quick Sign
|
|
702
|
+
const quickBtn = h('button', { className: 'sdt-ov-btn sdt-ov-btn-primary sdt-ov-btn-wide' }, loading ? 'Working\u2026' : 'Quick Sign Up');
|
|
708
703
|
quickBtn.disabled = loading;
|
|
709
704
|
quickBtn.addEventListener('click', () => {
|
|
710
705
|
runAsynchronously(doQuickSignIn());
|
|
711
706
|
});
|
|
712
707
|
actions.appendChild(quickBtn);
|
|
713
708
|
}
|
|
714
|
-
|
|
715
|
-
actions.appendChild(emailRow);
|
|
709
|
+
|
|
716
710
|
}
|
|
717
711
|
|
|
718
712
|
async function doQuickSignIn() {
|
|
@@ -744,54 +738,6 @@ function createOverviewTab(app: StackClientApp<true>): TabResult {
|
|
|
744
738
|
await refreshUser();
|
|
745
739
|
}
|
|
746
740
|
|
|
747
|
-
async function doSignInAs(targetEmail: string) {
|
|
748
|
-
if (!targetEmail.trim()) return;
|
|
749
|
-
if (!isLocalhost(window.location.href)) {
|
|
750
|
-
showToast('Quick sign-in is only available on localhost', 'error');
|
|
751
|
-
return;
|
|
752
|
-
}
|
|
753
|
-
loading = true;
|
|
754
|
-
rebuildActions();
|
|
755
|
-
const trimmed = targetEmail.trim();
|
|
756
|
-
try {
|
|
757
|
-
const signInResult = await app.signInWithCredential({ email: trimmed, password: trimmed, noRedirect: true });
|
|
758
|
-
if (signInResult.status === 'ok') {
|
|
759
|
-
showToast(`Signed in as ${trimmed}`, 'success');
|
|
760
|
-
emailInput.value = '';
|
|
761
|
-
loading = false;
|
|
762
|
-
await refreshUser();
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
765
|
-
const signUpResult = await app.signUpWithCredential({ email: trimmed, password: trimmed, noRedirect: true } as any);
|
|
766
|
-
if (signUpResult.status === 'error') {
|
|
767
|
-
showToast(`Failed: ${signUpResult.error.message}`, 'error');
|
|
768
|
-
loading = false;
|
|
769
|
-
rebuildActions();
|
|
770
|
-
return;
|
|
771
|
-
}
|
|
772
|
-
const retryResult = await app.signInWithCredential({ email: trimmed, password: trimmed, noRedirect: true });
|
|
773
|
-
if (retryResult.status === 'error') {
|
|
774
|
-
showToast(`Sign in failed: ${retryResult.error.message}`, 'error');
|
|
775
|
-
} else {
|
|
776
|
-
showToast(`Signed in as ${trimmed}`, 'success');
|
|
777
|
-
emailInput.value = '';
|
|
778
|
-
}
|
|
779
|
-
} catch (e: any) {
|
|
780
|
-
showToast(e.message || 'Unknown error', 'error');
|
|
781
|
-
}
|
|
782
|
-
loading = false;
|
|
783
|
-
await refreshUser();
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
emailBtn.addEventListener('click', () => {
|
|
787
|
-
runAsynchronously(doSignInAs(emailInput.value));
|
|
788
|
-
});
|
|
789
|
-
emailInput.addEventListener('keydown', (e) => {
|
|
790
|
-
if (e.key === 'Enter') {
|
|
791
|
-
runAsynchronously(doSignInAs(emailInput.value));
|
|
792
|
-
}
|
|
793
|
-
});
|
|
794
|
-
|
|
795
741
|
heroCard.append(actions, toast);
|
|
796
742
|
|
|
797
743
|
// ── Auth methods card ──────────────────────────────────────────────────────
|
|
@@ -855,7 +801,7 @@ function createOverviewTab(app: StackClientApp<true>): TabResult {
|
|
|
855
801
|
function buildChecklist() {
|
|
856
802
|
checksCard.innerHTML = '';
|
|
857
803
|
const currentUserCheck = hasPersistentTokenStore
|
|
858
|
-
? { ok: !!currentUser, label: 'Sign in a test user', hint: 'Use \u201cQuick Sign
|
|
804
|
+
? { ok: !!currentUser, label: 'Sign in a test user', hint: 'Use \u201cQuick Sign Up\u201d above \u2192' }
|
|
859
805
|
: { ok: true, label: 'Current-user tools unavailable', hint: null };
|
|
860
806
|
const checks = [
|
|
861
807
|
{ ok: !!projectId && projectId !== 'default', label: 'Project configured', hint: null },
|
|
@@ -925,7 +871,7 @@ function createOverviewTab(app: StackClientApp<true>): TabResult {
|
|
|
925
871
|
} else {
|
|
926
872
|
avatar.textContent = initials;
|
|
927
873
|
}
|
|
928
|
-
userName.textContent = currentUser.displayName || '
|
|
874
|
+
userName.textContent = currentUser.displayName || '(No display name)';
|
|
929
875
|
userEmail.textContent = currentUser.primaryEmail || 'No email';
|
|
930
876
|
authIndicator.style.display = '';
|
|
931
877
|
} else {
|
|
@@ -1905,7 +1851,7 @@ function createSupportTab(app: StackClientApp<true>): HTMLElement {
|
|
|
1905
1851
|
function createComponentsTab(app: StackClientApp<true>): HTMLElement {
|
|
1906
1852
|
const container = h('div', { className: 'sdt-pg-layout' });
|
|
1907
1853
|
const apiBaseUrl = resolveApiBaseUrl(app);
|
|
1908
|
-
const urls = app.
|
|
1854
|
+
const urls = app[hexclaveAppInternalsSymbol].getUrls();
|
|
1909
1855
|
const urlOptions: HandlerUrlOptions = app[hexclaveAppInternalsSymbol].getConstructorOptions().urls ?? {};
|
|
1910
1856
|
|
|
1911
1857
|
const PAGE_ENTRIES: { key: keyof HandlerUrls; label: string }[] = [
|
|
@@ -24,6 +24,7 @@ import { EmailConfig, hexclaveAppInternalsSymbol } from "../../common";
|
|
|
24
24
|
import { AdminEmailTemplate } from "../../email-templates";
|
|
25
25
|
import { InternalApiKey, InternalApiKeyBase, InternalApiKeyBaseCrudRead, InternalApiKeyCreateOptions, InternalApiKeyFirstView, internalApiKeyCreateOptionsToCrud } from "../../internal-api-keys";
|
|
26
26
|
import { AdminProjectPermission, AdminProjectPermissionDefinition, AdminProjectPermissionDefinitionCreateOptions, AdminProjectPermissionDefinitionUpdateOptions, AdminTeamPermission, AdminTeamPermissionDefinition, AdminTeamPermissionDefinitionCreateOptions, AdminTeamPermissionDefinitionUpdateOptions, adminProjectPermissionDefinitionCreateOptionsToCrud, adminProjectPermissionDefinitionUpdateOptionsToCrud, adminTeamPermissionDefinitionCreateOptionsToCrud, adminTeamPermissionDefinitionUpdateOptionsToCrud } from "../../permissions";
|
|
27
|
+
import type { PlanUsage } from "../../plan-usage";
|
|
27
28
|
import { AdminOwnedProject, AdminProject, AdminProjectUpdateOptions, PushConfigOptions, adminProjectUpdateOptionsToCrud } from "../../projects";
|
|
28
29
|
import type { AdminSessionReplay, AdminSessionReplayChunk, ListSessionReplayChunksOptions, ListSessionReplayChunksResult, ListSessionReplaysOptions, ListSessionReplaysResult, SessionReplayAllEventsResult } from "../../session-replays";
|
|
29
30
|
import { ManagedEmailProviderListItem, ManagedEmailProviderSetupResult, ManagedEmailProviderStatus, EmailOutboxUpdateOptions, StackAdminApp, StackAdminAppConstructorOptions } from "../interfaces/admin-app";
|
|
@@ -37,6 +38,7 @@ import { PushedConfigSource } from "../../projects";
|
|
|
37
38
|
import { useAsyncCache } from "./common"; // THIS_LINE_PLATFORM react-like
|
|
38
39
|
|
|
39
40
|
type BranchConfigSourceApi = yup.InferType<typeof branchConfigSourceSchema>;
|
|
41
|
+
type PlanUsageResponse = Awaited<ReturnType<HexclaveAdminInterface["getPlanUsage"]>>;
|
|
40
42
|
/**
|
|
41
43
|
* Converts a PushedConfigSource (SDK camelCase) to BranchConfigSourceApi (API snake_case).
|
|
42
44
|
*/
|
|
@@ -79,6 +81,9 @@ export class _HexclaveAdminAppImplIncomplete<HasTokenStore extends boolean, Proj
|
|
|
79
81
|
private readonly _adminProjectCache = createCache(async () => {
|
|
80
82
|
return await this._interface.getProject();
|
|
81
83
|
});
|
|
84
|
+
private readonly _planUsageCache = createCache(async () => {
|
|
85
|
+
return await this._interface.getPlanUsage();
|
|
86
|
+
});
|
|
82
87
|
private readonly _internalApiKeysCache = createCache(async () => {
|
|
83
88
|
const res = await this._interface.listInternalApiKeys();
|
|
84
89
|
return res;
|
|
@@ -196,10 +201,17 @@ export class _HexclaveAdminAppImplIncomplete<HasTokenStore extends boolean, Proj
|
|
|
196
201
|
isDevelopmentEnvironment: data.is_development_environment,
|
|
197
202
|
ownerTeamId: data.owner_team_id,
|
|
198
203
|
onboardingStatus: data.onboarding_status,
|
|
204
|
+
onboardingState: data.onboarding_state ?? null,
|
|
199
205
|
logoUrl: data.logo_url,
|
|
200
206
|
logoFullUrl: data.logo_full_url,
|
|
201
207
|
logoDarkModeUrl: data.logo_dark_mode_url,
|
|
202
208
|
logoFullDarkModeUrl: data.logo_full_dark_mode_url,
|
|
209
|
+
pushedConfigError: data.pushed_config_error == null ? null : {
|
|
210
|
+
message: data.pushed_config_error.message,
|
|
211
|
+
},
|
|
212
|
+
configWarnings: data.config_warnings.map((warning) => ({
|
|
213
|
+
message: warning.message,
|
|
214
|
+
})),
|
|
203
215
|
config: {
|
|
204
216
|
signUpEnabled: data.config.sign_up_enabled,
|
|
205
217
|
credentialEnabled: data.config.credential_enabled,
|
|
@@ -331,6 +343,28 @@ export class _HexclaveAdminAppImplIncomplete<HasTokenStore extends boolean, Proj
|
|
|
331
343
|
};
|
|
332
344
|
}
|
|
333
345
|
|
|
346
|
+
_planUsageFromCrud(data: PlanUsageResponse): PlanUsage {
|
|
347
|
+
return {
|
|
348
|
+
ownerTeamId: data.owner_team_id,
|
|
349
|
+
ownerTeamDisplayName: data.owner_team_display_name,
|
|
350
|
+
planId: data.plan_id,
|
|
351
|
+
planDisplayName: data.plan_display_name,
|
|
352
|
+
periodStart: new Date(data.period_start_millis),
|
|
353
|
+
periodEnd: new Date(data.period_end_millis),
|
|
354
|
+
nextPlanId: data.next_plan_id,
|
|
355
|
+
rows: data.rows.map((row) => ({
|
|
356
|
+
itemId: row.item_id,
|
|
357
|
+
displayName: row.display_name,
|
|
358
|
+
kind: row.kind,
|
|
359
|
+
used: row.used,
|
|
360
|
+
limit: row.limit,
|
|
361
|
+
remaining: row.remaining,
|
|
362
|
+
overage: row.overage,
|
|
363
|
+
isUnlimited: row.is_unlimited,
|
|
364
|
+
})),
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
334
368
|
override async getProject(): Promise<AdminProject> {
|
|
335
369
|
return this._adminProjectFromCrud(
|
|
336
370
|
Result.orThrow(await this._adminProjectCache.getOrWait([], "write-only")),
|
|
@@ -346,6 +380,15 @@ export class _HexclaveAdminAppImplIncomplete<HasTokenStore extends boolean, Proj
|
|
|
346
380
|
), [crud]);
|
|
347
381
|
}
|
|
348
382
|
|
|
383
|
+
async getPlanUsage(): Promise<PlanUsage> {
|
|
384
|
+
return this._planUsageFromCrud(Result.orThrow(await this._planUsageCache.getOrWait([], "write-only")));
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
usePlanUsage(): PlanUsage {
|
|
388
|
+
const crud = useAsyncCache(this._planUsageCache, [], "adminApp.usePlanUsage()");
|
|
389
|
+
return useMemo(() => this._planUsageFromCrud(crud), [crud]);
|
|
390
|
+
}
|
|
391
|
+
|
|
349
392
|
protected _createInternalApiKeyBaseFromCrud(data: InternalApiKeyBaseCrudRead): InternalApiKeyBase {
|
|
350
393
|
const app = this;
|
|
351
394
|
return {
|
|
@@ -1140,7 +1183,7 @@ export class _HexclaveAdminAppImplIncomplete<HasTokenStore extends boolean, Proj
|
|
|
1140
1183
|
}
|
|
1141
1184
|
|
|
1142
1185
|
async getStripeAccountInfo(): Promise<null | { account_id: string, charges_enabled: boolean, details_submitted: boolean, payouts_enabled: boolean }> {
|
|
1143
|
-
return await this.
|
|
1186
|
+
return Result.orThrow(await this._stripeAccountInfoCache.getOrWait([], "write-only"));
|
|
1144
1187
|
}
|
|
1145
1188
|
|
|
1146
1189
|
useStripeAccountInfo(): { account_id: string, charges_enabled: boolean, details_submitted: boolean, payouts_enabled: boolean } | null {
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
//===========================================
|
|
3
3
|
// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template
|
|
4
4
|
//===========================================
|
|
5
|
-
import {
|
|
6
|
-
import { KnownError, KnownErrors, HexclaveClientInterface } from "@hexclave/shared";
|
|
5
|
+
import { HexclaveClientInterface, KnownError, KnownErrors } from "@hexclave/shared";
|
|
7
6
|
import type { RequestListener } from "@hexclave/shared/dist/interface/client-interface";
|
|
8
7
|
import { ContactChannelsCrud } from "@hexclave/shared/dist/interface/crud/contact-channels";
|
|
9
8
|
import { CurrentUserCrud } from "@hexclave/shared/dist/interface/crud/current-user";
|
|
@@ -42,14 +41,15 @@ import { BotChallengeExecutionFailedError, BotChallengeUserCancelledError, withB
|
|
|
42
41
|
import { createUrlIfValid, getRelativePart, isRelative } from "@hexclave/shared/dist/utils/urls";
|
|
43
42
|
import { generateUuid } from "@hexclave/shared/dist/utils/uuids";
|
|
44
43
|
import * as tanstackStartServerContext from "@hexclave/tanstack-start/tanstack-start-server-context"; // THIS_LINE_PLATFORM tanstack-start
|
|
44
|
+
import { WebAuthnError, startAuthentication, startRegistration } from "@simplewebauthn/browser";
|
|
45
45
|
import * as TanStackRouter from "@tanstack/react-router"; // THIS_LINE_PLATFORM tanstack-start
|
|
46
46
|
import * as cookie from "cookie";
|
|
47
47
|
import React, { useCallback, useMemo } from "react"; // THIS_LINE_PLATFORM react-like
|
|
48
48
|
import type * as yup from "yup";
|
|
49
|
+
import { envVars } from "../../../../generated/env";
|
|
49
50
|
import { constructRedirectUrl } from "../../../../utils/url";
|
|
50
51
|
import { callOAuthCallback, getNewOAuthProviderOrScopeUrl } from "../../../auth";
|
|
51
52
|
import { CookieHelper, createBrowserCookieHelper, createCookieHelper, createPlaceholderCookieHelper, deleteCookie, deleteCookieClient, getCookieClient, isSecure as isSecureCookieContext, saveVerifierAndState, setOrDeleteCookie, setOrDeleteCookieClient } from "../../../cookie";
|
|
52
|
-
import { envVars } from "../../../../generated/env";
|
|
53
53
|
import { ApiKey, ApiKeyCreationOptions, ApiKeyUpdateOptions, apiKeyCreationOptionsToCrud } from "../../api-keys";
|
|
54
54
|
import { ConvexCtx, GetCurrentPartialUserOptions, GetCurrentUserOptions, HandlerUrlOptions, HandlerUrls, OAuthScopesOnSignIn, RedirectMethod, RedirectToOptions, RequestLike, ResolvedHandlerUrls, TokenStoreInit, hexclaveAppInternalsSymbol } from "../../common";
|
|
55
55
|
import { DeprecatedOAuthConnection, OAuthConnection } from "../../connected-accounts";
|
|
@@ -73,6 +73,7 @@ import { AnalyticsOptions, SessionRecorder, analyticsOptionsFromJson, analyticsO
|
|
|
73
73
|
import { useAsyncCache } from "./common";
|
|
74
74
|
import { mountClickmapOverlay } from "../../../../clickmap";
|
|
75
75
|
import { mountDevTool } from "../../../../dev-tool";
|
|
76
|
+
import { mountPushedConfigErrorOverlay } from "../../../../pushed-config-error-overlay";
|
|
76
77
|
|
|
77
78
|
let isReactServer = false;
|
|
78
79
|
|
|
@@ -356,7 +357,7 @@ export class _HexclaveClientAppImplIncomplete<HasTokenStore extends boolean, Pro
|
|
|
356
357
|
{
|
|
357
358
|
provider,
|
|
358
359
|
redirectUrl: this._getOAuthCallbackRedirectUri(),
|
|
359
|
-
errorRedirectUrl: this.
|
|
360
|
+
errorRedirectUrl: this._getUrls().error,
|
|
360
361
|
providerScope: mergeScopeStrings(scopeString, (this._oauthScopesOnSignIn[provider as ProviderType] ?? []).join(" ")),
|
|
361
362
|
},
|
|
362
363
|
session,
|
|
@@ -559,7 +560,7 @@ export class _HexclaveClientAppImplIncomplete<HasTokenStore extends boolean, Pro
|
|
|
559
560
|
{
|
|
560
561
|
provider: options.providerId,
|
|
561
562
|
redirectUrl: this._getOAuthCallbackRedirectUri(),
|
|
562
|
-
errorRedirectUrl: this.
|
|
563
|
+
errorRedirectUrl: this._getUrls().error,
|
|
563
564
|
providerScope: mergeScopeStrings(options.scope || "", (this._oauthScopesOnSignIn[options.providerId] ?? []).join(" ")),
|
|
564
565
|
},
|
|
565
566
|
options.session,
|
|
@@ -742,6 +743,7 @@ export class _HexclaveClientAppImplIncomplete<HasTokenStore extends boolean, Pro
|
|
|
742
743
|
// when a dashboard-minted token is handed over, so the listener is
|
|
743
744
|
// mounted unconditionally (the heavy UI is lazy-loaded on demand).
|
|
744
745
|
mountClickmapOverlay(this as any);
|
|
746
|
+
mountPushedConfigErrorOverlay(this as any);
|
|
745
747
|
}
|
|
746
748
|
}
|
|
747
749
|
|
|
@@ -863,7 +865,7 @@ export class _HexclaveClientAppImplIncomplete<HasTokenStore extends boolean, Pro
|
|
|
863
865
|
|
|
864
866
|
protected _getOAuthCallbackRedirectUri(): string {
|
|
865
867
|
if (!this._isOAuthCallbackUrlHosted()) {
|
|
866
|
-
return this.
|
|
868
|
+
return this._getUrls().oauthCallback;
|
|
867
869
|
}
|
|
868
870
|
if (typeof window === "undefined") {
|
|
869
871
|
throw new HexclaveAssertionError("Hosted OAuth callback URLs require a browser environment to use the current URL as the redirect URI");
|
|
@@ -1636,6 +1638,12 @@ export class _HexclaveClientAppImplIncomplete<HasTokenStore extends boolean, Pro
|
|
|
1636
1638
|
return {
|
|
1637
1639
|
id: crud.id,
|
|
1638
1640
|
displayName: crud.display_name,
|
|
1641
|
+
pushedConfigError: crud.pushed_config_error == null ? null : {
|
|
1642
|
+
message: crud.pushed_config_error.message,
|
|
1643
|
+
},
|
|
1644
|
+
configWarnings: crud.config_warnings.map((warning) => ({
|
|
1645
|
+
message: warning.message,
|
|
1646
|
+
})),
|
|
1639
1647
|
config: {
|
|
1640
1648
|
signUpEnabled: crud.config.sign_up_enabled,
|
|
1641
1649
|
credentialEnabled: crud.config.credential_enabled,
|
|
@@ -3030,13 +3038,13 @@ export class _HexclaveClientAppImplIncomplete<HasTokenStore extends boolean, Pro
|
|
|
3030
3038
|
async redirectToMfa(options?: RedirectToOptions) { return await this._redirectToHandler("mfa", options); }
|
|
3031
3039
|
|
|
3032
3040
|
async sendForgotPasswordEmail(email: string, options?: { callbackUrl?: string }): Promise<Result<undefined, KnownErrors["UserNotFound"]>> {
|
|
3033
|
-
return await this._interface.sendForgotPasswordEmail(email, options?.callbackUrl ?? constructRedirectUrl(this.
|
|
3041
|
+
return await this._interface.sendForgotPasswordEmail(email, options?.callbackUrl ?? constructRedirectUrl(this._getUrls().passwordReset, "callbackUrl"));
|
|
3034
3042
|
}
|
|
3035
3043
|
|
|
3036
3044
|
async sendMagicLinkEmail(email: string, options?: {
|
|
3037
3045
|
callbackUrl?: string,
|
|
3038
3046
|
}): Promise<Result<{ nonce: string }, KnownErrors["RedirectUrlNotWhitelisted"] | KnownErrors["BotChallengeFailed"]>> {
|
|
3039
|
-
const callbackUrl = options?.callbackUrl ?? constructRedirectUrl(this.
|
|
3047
|
+
const callbackUrl = options?.callbackUrl ?? constructRedirectUrl(this._getUrls().magicLinkCallback, "callbackUrl");
|
|
3040
3048
|
return await this._executeResultWithBotChallengeFlow({
|
|
3041
3049
|
action: "send_magic_link_email",
|
|
3042
3050
|
execute: async (challenge) => {
|
|
@@ -3325,7 +3333,7 @@ export class _HexclaveClientAppImplIncomplete<HasTokenStore extends boolean, Pro
|
|
|
3325
3333
|
return await this._interface.authorizeOAuth({
|
|
3326
3334
|
provider,
|
|
3327
3335
|
redirectUrl: constructRedirectUrl(this._getOAuthCallbackRedirectUri(), "redirectUrl"),
|
|
3328
|
-
errorRedirectUrl: constructRedirectUrl(this.
|
|
3336
|
+
errorRedirectUrl: constructRedirectUrl(this._getUrls().error, "errorRedirectUrl"),
|
|
3329
3337
|
afterCallbackRedirectUrl,
|
|
3330
3338
|
type: "authenticate",
|
|
3331
3339
|
providerScope: this._oauthScopesOnSignIn[provider]?.join(" "),
|
|
@@ -3445,7 +3453,7 @@ export class _HexclaveClientAppImplIncomplete<HasTokenStore extends boolean, Pro
|
|
|
3445
3453
|
}
|
|
3446
3454
|
this._ensurePersistentTokenStore();
|
|
3447
3455
|
const session = await this._getSession();
|
|
3448
|
-
const emailVerificationRedirectUrl = options.noVerificationCallback ? undefined : options.verificationCallbackUrl ?? constructRedirectUrl(this.
|
|
3456
|
+
const emailVerificationRedirectUrl = options.noVerificationCallback ? undefined : options.verificationCallbackUrl ?? constructRedirectUrl(this._getUrls().emailVerification, "verificationCallbackUrl");
|
|
3449
3457
|
|
|
3450
3458
|
const executeSignUp = async (challenge: { token?: string, phase?: "invisible" | "visible", unavailable?: true }) => {
|
|
3451
3459
|
let result = await this._interface.signUpWithCredential(
|
|
@@ -3608,7 +3616,7 @@ export class _HexclaveClientAppImplIncomplete<HasTokenStore extends boolean, Pro
|
|
|
3608
3616
|
|
|
3609
3617
|
// Step 2: Open the browser for the user to authenticate and display the verification code
|
|
3610
3618
|
const url = buildCliAuthConfirmUrl({
|
|
3611
|
-
cliAuthConfirmUrl: this.
|
|
3619
|
+
cliAuthConfirmUrl: this._getUrls().cliAuthConfirm,
|
|
3612
3620
|
appUrl: options.appUrl,
|
|
3613
3621
|
loginCode,
|
|
3614
3622
|
});
|
|
@@ -4046,6 +4054,7 @@ export class _HexclaveClientAppImplIncomplete<HasTokenStore extends boolean, Pro
|
|
|
4046
4054
|
) => {
|
|
4047
4055
|
return await this._interface.sendClientRequest(path, requestOptions, await this._getSession(), requestType);
|
|
4048
4056
|
},
|
|
4057
|
+
getUrls: () => this._getUrls(),
|
|
4049
4058
|
getRedirectMethod: () => this._redirectMethod ?? throwErr("Redirect method should have been initialized in the Stack client app constructor"),
|
|
4050
4059
|
redirectToUrl: async (url: string | URL, options?: { replace?: boolean }) => {
|
|
4051
4060
|
await this._redirectTo({ url, ...options });
|
|
@@ -391,6 +391,39 @@ describe("EventTracker", () => {
|
|
|
391
391
|
}
|
|
392
392
|
});
|
|
393
393
|
|
|
394
|
+
it("silently ignores network errors caused by ad blockers", async () => {
|
|
395
|
+
vi.useFakeTimers();
|
|
396
|
+
document.body.innerHTML = "<button>Click me</button>";
|
|
397
|
+
|
|
398
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
399
|
+
const sentBodies: string[] = [];
|
|
400
|
+
const tracker = new EventTracker({
|
|
401
|
+
projectId: "internal",
|
|
402
|
+
sendBatch: async (body) => {
|
|
403
|
+
sentBodies.push(body);
|
|
404
|
+
return Result.error(new TypeError("Failed to fetch"));
|
|
405
|
+
},
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
tracker.start();
|
|
410
|
+
|
|
411
|
+
await advancePastFlush();
|
|
412
|
+
expect(sentBodies).toHaveLength(1);
|
|
413
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
414
|
+
|
|
415
|
+
// Unlike ANALYTICS_NOT_ENABLED, ad blocker errors do NOT disable the
|
|
416
|
+
// tracker — subsequent flushes continue attempting delivery.
|
|
417
|
+
document.querySelector("button")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
|
418
|
+
await advancePastFlush();
|
|
419
|
+
expect(sentBodies).toHaveLength(2);
|
|
420
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
421
|
+
} finally {
|
|
422
|
+
tracker.stop();
|
|
423
|
+
warnSpy.mockRestore();
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
|
|
394
427
|
it("silently disables when client interface returns ANALYTICS_NOT_ENABLED as an error", async () => {
|
|
395
428
|
vi.useFakeTimers();
|
|
396
429
|
document.body.innerHTML = "<button>Click me</button>";
|
|
@@ -8,7 +8,7 @@ import { cssEscapeIdent } from "@hexclave/shared/dist/utils/dom";
|
|
|
8
8
|
import { buildElementsChain, ELEMENTS_CHAIN_MAX_DEPTH } from "@hexclave/shared/dist/utils/elements-chain";
|
|
9
9
|
import { runAsynchronously } from "@hexclave/shared/dist/utils/promises";
|
|
10
10
|
import { Result } from "@hexclave/shared/dist/utils/results";
|
|
11
|
-
import { generateUuid, isAnalyticsNotEnabledError } from "./session-replay";
|
|
11
|
+
import { generateUuid, isAdBlockerNetworkError, isAnalyticsNotEnabledError } from "./session-replay";
|
|
12
12
|
|
|
13
13
|
const FLUSH_INTERVAL_MS = 10_000;
|
|
14
14
|
const MAX_EVENTS_PER_BATCH = 50;
|
|
@@ -511,6 +511,11 @@ export class EventTracker {
|
|
|
511
511
|
this._disable();
|
|
512
512
|
return;
|
|
513
513
|
}
|
|
514
|
+
// Ad blockers commonly block analytics endpoints, causing network
|
|
515
|
+
// errors. These are expected and should not pollute the console.
|
|
516
|
+
if (isAdBlockerNetworkError(res.error)) {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
514
519
|
console.warn("EventTracker flush failed:", res.error);
|
|
515
520
|
return;
|
|
516
521
|
}
|
|
@@ -49,6 +49,58 @@ describe("analytics option JSON conversion", () => {
|
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
describe("SessionRecorder flush", () => {
|
|
52
|
+
it("silently ignores network errors caused by ad blockers", async () => {
|
|
53
|
+
vi.useFakeTimers();
|
|
54
|
+
|
|
55
|
+
const storageKey = `hexclave:session-replay:v1:test-project`;
|
|
56
|
+
localStorage.setItem(storageKey, JSON.stringify({
|
|
57
|
+
session_id: "test-session",
|
|
58
|
+
created_at_ms: Date.now(),
|
|
59
|
+
last_activity_ms: Date.now(),
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
const sentBodies: string[] = [];
|
|
63
|
+
const recorder = new SessionRecorder(
|
|
64
|
+
{
|
|
65
|
+
projectId: "test-project",
|
|
66
|
+
sendBatch: async (body) => {
|
|
67
|
+
sentBodies.push(body);
|
|
68
|
+
return Result.error(new TypeError("Failed to fetch"));
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
{},
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
78
|
+
(recorder as any)._events = [{ type: 2, timestamp: Date.now(), data: {} }];
|
|
79
|
+
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
|
81
|
+
(recorder as any)._tick();
|
|
82
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
83
|
+
|
|
84
|
+
expect(sentBodies).toHaveLength(1);
|
|
85
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
86
|
+
|
|
87
|
+
// Unlike ANALYTICS_NOT_ENABLED, ad blocker errors do NOT disable the
|
|
88
|
+
// recorder — subsequent flushes continue attempting delivery.
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
90
|
+
(recorder as any)._events = [{ type: 3, timestamp: Date.now(), data: {} }];
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
|
92
|
+
(recorder as any)._tick();
|
|
93
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
94
|
+
expect(sentBodies).toHaveLength(2);
|
|
95
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
96
|
+
} finally {
|
|
97
|
+
recorder.stop();
|
|
98
|
+
warnSpy.mockRestore();
|
|
99
|
+
localStorage.removeItem(storageKey);
|
|
100
|
+
vi.useRealTimers();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
52
104
|
it("silently disables when client interface returns ANALYTICS_NOT_ENABLED as an error", async () => {
|
|
53
105
|
vi.useFakeTimers();
|
|
54
106
|
|
|
@@ -170,6 +170,21 @@ export function isAnalyticsNotEnabledError(error: unknown): boolean {
|
|
|
170
170
|
return KnownErrors.AnalyticsNotEnabled.isInstance(error);
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Whether the error looks like a network failure caused by an ad blocker or
|
|
175
|
+
* similar extension blocking analytics requests. These are expected in
|
|
176
|
+
* production and should be silently ignored rather than logged as warnings.
|
|
177
|
+
*/
|
|
178
|
+
export function isAdBlockerNetworkError(error: unknown): boolean {
|
|
179
|
+
if (error instanceof Error) {
|
|
180
|
+
return error.message.includes("Failed to fetch")
|
|
181
|
+
|| error.message.includes("NetworkError")
|
|
182
|
+
|| error.message.includes("Load failed")
|
|
183
|
+
|| error.message.includes("network connection");
|
|
184
|
+
}
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
|
|
173
188
|
export class SessionRecorder {
|
|
174
189
|
private _started = false;
|
|
175
190
|
private _cancelled = false;
|
|
@@ -278,6 +293,11 @@ export class SessionRecorder {
|
|
|
278
293
|
this._disable();
|
|
279
294
|
return;
|
|
280
295
|
}
|
|
296
|
+
// Ad blockers commonly block analytics endpoints, causing network
|
|
297
|
+
// errors. These are expected and should not pollute the console.
|
|
298
|
+
if (isAdBlockerNetworkError(res.error)) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
281
301
|
captureWarning("SessionRecorder.flush", res.error);
|
|
282
302
|
return;
|
|
283
303
|
}
|
|
@@ -13,6 +13,7 @@ import { AsyncStoreProperty, EmailConfig } from "../../common";
|
|
|
13
13
|
import { AdminEmailOutbox, AdminSentEmail } from "../../email";
|
|
14
14
|
import { InternalApiKey, InternalApiKeyCreateOptions, InternalApiKeyFirstView } from "../../internal-api-keys";
|
|
15
15
|
import { AdminProjectPermission, AdminProjectPermissionDefinition, AdminProjectPermissionDefinitionCreateOptions, AdminProjectPermissionDefinitionUpdateOptions, AdminTeamPermission, AdminTeamPermissionDefinition, AdminTeamPermissionDefinitionCreateOptions, AdminTeamPermissionDefinitionUpdateOptions } from "../../permissions";
|
|
16
|
+
import type { PlanUsage } from "../../plan-usage";
|
|
16
17
|
import { AdminProject } from "../../projects";
|
|
17
18
|
import { _HexclaveAdminAppImpl } from "../implementations";
|
|
18
19
|
import { StackServerApp, StackServerAppConstructorOptions } from "./server-app";
|
|
@@ -75,6 +76,7 @@ export type StackAdminAppConstructorOptions<HasTokenStore extends boolean, Proje
|
|
|
75
76
|
/** @deprecated Use `HexclaveAdminApp` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */
|
|
76
77
|
export type StackAdminApp<HasTokenStore extends boolean = boolean, ProjectId extends string = string> = (
|
|
77
78
|
& AsyncStoreProperty<"project", [], AdminProject, false>
|
|
79
|
+
& AsyncStoreProperty<"planUsage", [], PlanUsage, false>
|
|
78
80
|
& AsyncStoreProperty<"internalApiKeys", [], InternalApiKey[], true>
|
|
79
81
|
& AsyncStoreProperty<"teamPermissionDefinitions", [], AdminTeamPermissionDefinition[], true>
|
|
80
82
|
& AsyncStoreProperty<"projectPermissionDefinitions", [], AdminProjectPermissionDefinition[], true>
|
|
@@ -131,6 +131,7 @@ export type StackClientApp<HasTokenStore extends boolean = boolean, ProjectId ex
|
|
|
131
131
|
sendAnalyticsEventBatch(body: string, options: { keepalive: boolean }): Promise<Result<Response, Error>>,
|
|
132
132
|
addRequestListener(listener: RequestListener): () => void,
|
|
133
133
|
sendRequest(path: string, requestOptions: RequestInit, requestType?: "client" | "server" | "admin"): Promise<Response>,
|
|
134
|
+
getUrls(): Readonly<ResolvedHandlerUrls>,
|
|
134
135
|
getRedirectMethod(): RedirectMethod,
|
|
135
136
|
redirectToUrl(url: string | URL, options?: { replace?: boolean }): Promise<void>,
|
|
136
137
|
redirectToHandler(handlerName: keyof HandlerUrls, options?: RedirectToOptions): Promise<void>,
|
|
@@ -118,6 +118,14 @@ export type {
|
|
|
118
118
|
PushedConfigSource
|
|
119
119
|
} from "./projects";
|
|
120
120
|
|
|
121
|
+
export type {
|
|
122
|
+
PlanUsage,
|
|
123
|
+
PlanUsageKind,
|
|
124
|
+
PlanUsageNextPlanId,
|
|
125
|
+
PlanUsagePlanId,
|
|
126
|
+
PlanUsageRow,
|
|
127
|
+
} from "./plan-usage";
|
|
128
|
+
|
|
121
129
|
export type {
|
|
122
130
|
EditableTeamMemberProfile, ReceivedTeamInvitation,
|
|
123
131
|
SentTeamInvitation, ServerListUsersOptions,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
|
|
2
|
+
//===========================================
|
|
3
|
+
// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template
|
|
4
|
+
//===========================================
|
|
5
|
+
export type PlanUsageKind = "current" | "metered" | "capability";
|
|
6
|
+
export type PlanUsagePlanId = "free" | "team" | "growth";
|
|
7
|
+
export type PlanUsageNextPlanId = "team" | "growth";
|
|
8
|
+
|
|
9
|
+
export type PlanUsageRow = {
|
|
10
|
+
itemId: string,
|
|
11
|
+
displayName: string,
|
|
12
|
+
kind: PlanUsageKind,
|
|
13
|
+
used: number | null,
|
|
14
|
+
limit: number | null,
|
|
15
|
+
remaining: number | null,
|
|
16
|
+
overage: number | null,
|
|
17
|
+
isUnlimited: boolean,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type PlanUsage = {
|
|
21
|
+
ownerTeamId: string,
|
|
22
|
+
ownerTeamDisplayName: string,
|
|
23
|
+
planId: PlanUsagePlanId,
|
|
24
|
+
planDisplayName: string,
|
|
25
|
+
periodStart: Date,
|
|
26
|
+
periodEnd: Date,
|
|
27
|
+
nextPlanId: PlanUsageNextPlanId | null,
|
|
28
|
+
rows: PlanUsageRow[],
|
|
29
|
+
};
|
|
@@ -30,6 +30,8 @@ export type PushConfigOptions = {
|
|
|
30
30
|
export type Project = {
|
|
31
31
|
readonly id: string,
|
|
32
32
|
readonly displayName: string,
|
|
33
|
+
readonly pushedConfigError: { message: string } | null,
|
|
34
|
+
readonly configWarnings: { message: string }[],
|
|
33
35
|
readonly config: ProjectConfig,
|
|
34
36
|
};
|
|
35
37
|
|
|
@@ -42,6 +44,7 @@ export type AdminProject = {
|
|
|
42
44
|
readonly isDevelopmentEnvironment: boolean,
|
|
43
45
|
readonly ownerTeamId: string | null,
|
|
44
46
|
readonly onboardingStatus: ProjectOnboardingStatus,
|
|
47
|
+
readonly onboardingState: NonNullable<ProjectsCrud["Admin"]["Read"]["onboarding_state"]> | null,
|
|
45
48
|
readonly logoUrl: string | null | undefined,
|
|
46
49
|
readonly logoFullUrl: string | null | undefined,
|
|
47
50
|
readonly logoDarkModeUrl: string | null | undefined,
|