@hexclave/next 1.0.20 → 1.0.22
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 +187 -7
- package/dist/components/elements/sidebar-layout.js +1 -1
- package/dist/components-page/oauth-callback.js +14 -19
- package/dist/components-page/oauth-callback.js.map +1 -1
- package/dist/components-page/oauth-callback.test.d.ts +1 -0
- package/dist/components-page/oauth-callback.test.js +90 -0
- package/dist/components-page/oauth-callback.test.js.map +1 -0
- package/dist/esm/components/elements/sidebar-layout.js +1 -1
- package/dist/esm/components-page/oauth-callback.js +14 -19
- package/dist/esm/components-page/oauth-callback.js.map +1 -1
- package/dist/esm/components-page/oauth-callback.test.d.ts +1 -0
- package/dist/esm/components-page/oauth-callback.test.js +89 -0
- package/dist/esm/components-page/oauth-callback.test.js.map +1 -0
- package/dist/esm/generated/quetzal-translations.d.ts +2 -2
- package/dist/esm/lib/auth.d.ts.map +1 -1
- package/dist/esm/lib/auth.js +32 -11
- package/dist/esm/lib/auth.js.map +1 -1
- package/dist/esm/lib/auth.test.js +25 -10
- package/dist/esm/lib/auth.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -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 +1 -0
- 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.cross-domain.test.js +54 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +4 -1
- 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 +28 -4
- 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 +1 -0
- 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 +17 -13
- 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 +4 -8
- package/dist/esm/lib/hexclave-app/apps/implementations/event-tracker.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.d.ts +3 -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 +19 -13
- 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 +4 -9
- 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 +1 -0
- 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/generated/quetzal-translations.d.ts +2 -2
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +31 -10
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/auth.test.js +23 -8
- package/dist/lib/auth.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -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 +1 -0
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js +54 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +4 -1
- 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 +27 -3
- 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 +1 -0
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.js +16 -12
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.test.js +4 -8
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/session-replay.d.ts +3 -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 +19 -12
- package/dist/lib/hexclave-app/apps/implementations/session-replay.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/session-replay.test.js +4 -9
- 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 +1 -0
- 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/package.json +4 -4
- package/src/components-page/oauth-callback.test.tsx +109 -0
- package/src/components-page/oauth-callback.tsx +14 -19
- package/src/lib/auth.test.ts +32 -10
- package/src/lib/auth.ts +41 -7
- package/src/lib/hexclave-app/apps/implementations/admin-app-impl.ts +2 -1
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.ts +66 -0
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +39 -3
- package/src/lib/hexclave-app/apps/implementations/event-tracker.test.ts +5 -13
- package/src/lib/hexclave-app/apps/implementations/event-tracker.ts +19 -14
- package/src/lib/hexclave-app/apps/implementations/session-replay.test.ts +4 -20
- package/src/lib/hexclave-app/apps/implementations/session-replay.ts +19 -12
- package/src/lib/hexclave-app/apps/interfaces/admin-app.ts +1 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const require_chunk = require('../../../../chunk-BE-pF4vm.js');
|
|
2
2
|
let vitest = require("vitest");
|
|
3
3
|
let _hexclave_shared_dist_utils_results = require("@hexclave/shared/dist/utils/results");
|
|
4
|
+
let _hexclave_shared_dist_known_errors = require("@hexclave/shared/dist/known-errors");
|
|
4
5
|
let __session_replay_js = require("./session-replay.js");
|
|
5
6
|
|
|
6
7
|
//#region src/lib/hexclave-app/apps/implementations/session-replay.test.ts
|
|
@@ -37,7 +38,7 @@ let __session_replay_js = require("./session-replay.js");
|
|
|
37
38
|
});
|
|
38
39
|
});
|
|
39
40
|
(0, vitest.describe)("SessionRecorder flush", () => {
|
|
40
|
-
(0, vitest.it)("silently disables when
|
|
41
|
+
(0, vitest.it)("silently disables when client interface returns ANALYTICS_NOT_ENABLED as an error", async () => {
|
|
41
42
|
vitest.vi.useFakeTimers();
|
|
42
43
|
const storageKey = `hexclave:session-replay:v1:test-project`;
|
|
43
44
|
localStorage.setItem(storageKey, JSON.stringify({
|
|
@@ -50,13 +51,7 @@ let __session_replay_js = require("./session-replay.js");
|
|
|
50
51
|
projectId: "test-project",
|
|
51
52
|
sendBatch: async (body) => {
|
|
52
53
|
sentBodies.push(body);
|
|
53
|
-
return _hexclave_shared_dist_utils_results.Result.
|
|
54
|
-
code: "ANALYTICS_NOT_ENABLED",
|
|
55
|
-
error: "Analytics is not enabled for this project."
|
|
56
|
-
}), {
|
|
57
|
-
status: 400,
|
|
58
|
-
headers: { "x-stack-known-error": "ANALYTICS_NOT_ENABLED" }
|
|
59
|
-
}));
|
|
54
|
+
return _hexclave_shared_dist_utils_results.Result.error(new _hexclave_shared_dist_known_errors.KnownErrors.AnalyticsNotEnabled());
|
|
60
55
|
}
|
|
61
56
|
}, {});
|
|
62
57
|
const warnSpy = vitest.vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
@@ -69,7 +64,7 @@ let __session_replay_js = require("./session-replay.js");
|
|
|
69
64
|
recorder._tick();
|
|
70
65
|
await vitest.vi.advanceTimersByTimeAsync(0);
|
|
71
66
|
(0, vitest.expect)(sentBodies).toHaveLength(1);
|
|
72
|
-
(0, vitest.expect)(warnSpy
|
|
67
|
+
(0, vitest.expect)(warnSpy).not.toHaveBeenCalled();
|
|
73
68
|
recorder._events = [{
|
|
74
69
|
type: 3,
|
|
75
70
|
timestamp: Date.now(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-replay.test.js","names":["SessionRecorder","Result","vi"],"sources":["../../../../../src/lib/hexclave-app/apps/implementations/session-replay.test.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n// @vitest-environment jsdom\n\nimport { describe, expect, it, vi } from \"vitest\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport { analyticsOptionsFromJson, analyticsOptionsToJson, getSessionReplayOptions, SessionRecorder } from \"./session-replay\";\n\ndescribe(\"session replay options\", () => {\n it(\"enables replays by default\", () => {\n expect(getSessionReplayOptions(undefined).enabled).toBe(true);\n expect(getSessionReplayOptions({}).enabled).toBe(true);\n expect(getSessionReplayOptions({ replays: {} }).enabled).toBe(true);\n });\n\n it(\"preserves explicit replay opt-out\", () => {\n expect(getSessionReplayOptions({ replays: { enabled: false } }).enabled).toBe(false);\n });\n});\n\ndescribe(\"analytics option JSON conversion\", () => {\n it(\"preserves top-level analytics options when serializing replay block classes\", () => {\n const json = analyticsOptionsToJson({\n enabled: false,\n replays: {\n enabled: true,\n blockClass: /stack-sensitive/u,\n },\n });\n\n expect(json?.enabled).toBe(false);\n expect(json?.replays?.enabled).toBe(true);\n });\n\n it(\"preserves top-level analytics options when deserializing replay block classes\", () => {\n const roundTripped = analyticsOptionsFromJson(analyticsOptionsToJson({\n enabled: false,\n replays: {\n blockClass: /stack-sensitive/u,\n },\n }));\n\n expect(roundTripped?.enabled).toBe(false);\n expect(roundTripped?.replays?.blockClass).toEqual(/stack-sensitive/u);\n });\n});\n\ndescribe(\"SessionRecorder flush\", () => {\n it(\"silently disables when
|
|
1
|
+
{"version":3,"file":"session-replay.test.js","names":["SessionRecorder","Result","KnownErrors","vi"],"sources":["../../../../../src/lib/hexclave-app/apps/implementations/session-replay.test.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n// @vitest-environment jsdom\n\nimport { KnownErrors } from \"@hexclave/shared/dist/known-errors\";\nimport { describe, expect, it, vi } from \"vitest\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport { analyticsOptionsFromJson, analyticsOptionsToJson, getSessionReplayOptions, SessionRecorder } from \"./session-replay\";\n\ndescribe(\"session replay options\", () => {\n it(\"enables replays by default\", () => {\n expect(getSessionReplayOptions(undefined).enabled).toBe(true);\n expect(getSessionReplayOptions({}).enabled).toBe(true);\n expect(getSessionReplayOptions({ replays: {} }).enabled).toBe(true);\n });\n\n it(\"preserves explicit replay opt-out\", () => {\n expect(getSessionReplayOptions({ replays: { enabled: false } }).enabled).toBe(false);\n });\n});\n\ndescribe(\"analytics option JSON conversion\", () => {\n it(\"preserves top-level analytics options when serializing replay block classes\", () => {\n const json = analyticsOptionsToJson({\n enabled: false,\n replays: {\n enabled: true,\n blockClass: /stack-sensitive/u,\n },\n });\n\n expect(json?.enabled).toBe(false);\n expect(json?.replays?.enabled).toBe(true);\n });\n\n it(\"preserves top-level analytics options when deserializing replay block classes\", () => {\n const roundTripped = analyticsOptionsFromJson(analyticsOptionsToJson({\n enabled: false,\n replays: {\n blockClass: /stack-sensitive/u,\n },\n }));\n\n expect(roundTripped?.enabled).toBe(false);\n expect(roundTripped?.replays?.blockClass).toEqual(/stack-sensitive/u);\n });\n});\n\ndescribe(\"SessionRecorder flush\", () => {\n it(\"silently disables when client interface returns ANALYTICS_NOT_ENABLED as an error\", async () => {\n vi.useFakeTimers();\n\n const storageKey = `hexclave:session-replay:v1:test-project`;\n localStorage.setItem(storageKey, JSON.stringify({\n session_id: \"test-session\",\n created_at_ms: Date.now(),\n last_activity_ms: Date.now(),\n }));\n\n const sentBodies: string[] = [];\n const recorder = new SessionRecorder(\n {\n projectId: \"test-project\",\n sendBatch: async (body) => {\n sentBodies.push(body);\n return Result.error(new KnownErrors.AnalyticsNotEnabled());\n },\n },\n {},\n );\n\n const warnSpy = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._events = [{ type: 2, timestamp: Date.now(), data: {} }];\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n (recorder as any)._tick();\n await vi.advanceTimersByTimeAsync(0);\n\n expect(sentBodies).toHaveLength(1);\n expect(warnSpy).not.toHaveBeenCalled();\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._events = [{ type: 3, timestamp: Date.now(), data: {} }];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n (recorder as any)._tick();\n await vi.advanceTimersByTimeAsync(0);\n expect(sentBodies).toHaveLength(1);\n } finally {\n recorder.stop();\n warnSpy.mockRestore();\n localStorage.removeItem(storageKey);\n vi.useRealTimers();\n }\n });\n});\n"],"mappings":";;;;;;;;qBAWS,gCAAgC;AACvC,gBAAG,oCAAoC;AACrC,sEAA+B,OAAU,CAAC,QAAQ,CAAC,KAAK,KAAK;AAC7D,sEAA+B,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,KAAK;AACtD,sEAA+B,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,KAAK;GACnE;AAEF,gBAAG,2CAA2C;AAC5C,sEAA+B,EAAE,SAAS,EAAE,SAAS,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,MAAM;GACpF;EACF;qBAEO,0CAA0C;AACjD,gBAAG,qFAAqF;EACtF,MAAM,uDAA8B;GAClC,SAAS;GACT,SAAS;IACP,SAAS;IACT,YAAY;IACb;GACF,CAAC;AAEF,qBAAO,MAAM,QAAQ,CAAC,KAAK,MAAM;AACjC,qBAAO,MAAM,SAAS,QAAQ,CAAC,KAAK,KAAK;GACzC;AAEF,gBAAG,uFAAuF;EACxF,MAAM,iHAA+D;GACnE,SAAS;GACT,SAAS,EACP,YAAY,oBACb;GACF,CAAC,CAAC;AAEH,qBAAO,cAAc,QAAQ,CAAC,KAAK,MAAM;AACzC,qBAAO,cAAc,SAAS,WAAW,CAAC,QAAQ,mBAAmB;GACrE;EACF;qBAEO,+BAA+B;AACtC,gBAAG,qFAAqF,YAAY;AAClG,YAAG,eAAe;EAElB,MAAM,aAAa;AACnB,eAAa,QAAQ,YAAY,KAAK,UAAU;GAC9C,YAAY;GACZ,eAAe,KAAK,KAAK;GACzB,kBAAkB,KAAK,KAAK;GAC7B,CAAC,CAAC;EAEH,MAAM,aAAuB,EAAE;EAC/B,MAAM,WAAW,IAAIA,oCACnB;GACE,WAAW;GACX,WAAW,OAAO,SAAS;AACzB,eAAW,KAAK,KAAK;AACrB,WAAOC,2CAAO,MAAM,IAAIC,+CAAY,qBAAqB,CAAC;;GAE7D,EACD,EAAE,CACH;EAED,MAAM,UAAUC,UAAG,MAAM,SAAS,OAAO,CAAC,yBAAyB,GAAG;AAEtE,MAAI;AAEF,GAAC,SAAiB,UAAU,CAAC;IAAE,MAAM;IAAG,WAAW,KAAK,KAAK;IAAE,MAAM,EAAE;IAAE,CAAC;AAG1E,GAAC,SAAiB,OAAO;AACzB,SAAMA,UAAG,yBAAyB,EAAE;AAEpC,sBAAO,WAAW,CAAC,aAAa,EAAE;AAClC,sBAAO,QAAQ,CAAC,IAAI,kBAAkB;AAGtC,GAAC,SAAiB,UAAU,CAAC;IAAE,MAAM;IAAG,WAAW,KAAK,KAAK;IAAE,MAAM,EAAE;IAAE,CAAC;AAE1E,GAAC,SAAiB,OAAO;AACzB,SAAMA,UAAG,yBAAyB,EAAE;AACpC,sBAAO,WAAW,CAAC,aAAa,EAAE;YAC1B;AACR,YAAS,MAAM;AACf,WAAQ,aAAa;AACrB,gBAAa,WAAW,WAAW;AACnC,aAAG,eAAe;;GAEpB;EACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-app.d.ts","names":[],"sources":["../../../../../src/lib/hexclave-app/apps/interfaces/admin-app.ts"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"admin-app.d.ts","names":[],"sources":["../../../../../src/lib/hexclave-app/apps/interfaces/admin-app.ts"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-app.js","names":["_HexclaveAdminAppImpl"],"sources":["../../../../../src/lib/hexclave-app/apps/interfaces/admin-app.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport type { AnalyticsClickmapOptions, AnalyticsClickmapResponse, AnalyticsClickmapTokenResponse } from \"@hexclave/shared/dist/interface/admin-metrics\";\nimport { AnalyticsQueryOptions, AnalyticsQueryResponse } from \"@hexclave/shared/dist/interface/crud/analytics\";\nimport type { AdminGetSessionReplayChunkEventsResponse, AdminGetSessionReplayAllEventsResponse } from \"@hexclave/shared/dist/interface/crud/session-replays\";\nimport type { Transaction, TransactionType } from \"@hexclave/shared/dist/interface/crud/transactions\";\nimport { InternalSession } from \"@hexclave/shared/dist/sessions\";\nimport type { MoneyAmount } from \"@hexclave/shared/dist/utils/currency-constants\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport { AsyncStoreProperty, EmailConfig } from \"../../common\";\nimport { AdminEmailOutbox, AdminSentEmail } from \"../../email\";\nimport { InternalApiKey, InternalApiKeyCreateOptions, InternalApiKeyFirstView } from \"../../internal-api-keys\";\nimport { AdminProjectPermission, AdminProjectPermissionDefinition, AdminProjectPermissionDefinitionCreateOptions, AdminProjectPermissionDefinitionUpdateOptions, AdminTeamPermission, AdminTeamPermissionDefinition, AdminTeamPermissionDefinitionCreateOptions, AdminTeamPermissionDefinitionUpdateOptions } from \"../../permissions\";\nimport { AdminProject } from \"../../projects\";\nimport { _HexclaveAdminAppImpl } from \"../implementations\";\nimport { StackServerApp, StackServerAppConstructorOptions } from \"./server-app\";\n\nexport type EmailOutboxListOptions = {\n status?: string,\n simpleStatus?: string,\n limit?: number,\n cursor?: string,\n};\n\nexport type EmailOutboxListResult = {\n items: AdminEmailOutbox[],\n nextCursor: string | null,\n};\n\nexport type EmailOutboxUpdateOptions = {\n isPaused?: boolean,\n scheduledAtMillis?: number,\n cancel?: boolean,\n tsxSource?: string,\n themeId?: string | null,\n};\n\nexport type ManagedEmailProviderSetupResult = {\n domainId: string,\n subdomain: string,\n senderLocalPart: string,\n nameServerRecords: string[],\n status: ManagedEmailProviderStatus[\"status\"],\n};\n\nexport type ManagedEmailProviderStatus = {\n status: \"pending_dns\" | \"pending_verification\" | \"verified\" | \"applied\" | \"failed\",\n};\n\nexport type ManagedEmailProviderListItem = {\n domainId: string,\n subdomain: string,\n senderLocalPart: string,\n status: ManagedEmailProviderStatus[\"status\"],\n nameServerRecords: string[],\n};\n\nimport type { AdminSessionReplay, ListSessionReplayChunksOptions, ListSessionReplayChunksResult, ListSessionReplaysOptions, ListSessionReplaysResult, SessionReplayAllEventsResult } from \"../../session-replays\";\nexport type { AdminSessionReplay, AdminSessionReplayChunk, ListSessionReplaysOptions, ListSessionReplaysResult, ListSessionReplayChunksOptions, ListSessionReplayChunksResult, SessionReplayAllEventsResult } from \"../../session-replays\";\n\n\n/** @deprecated Use `HexclaveAdminAppConstructorOptions` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */\nexport type StackAdminAppConstructorOptions<HasTokenStore extends boolean, ProjectId extends string> = (\n & StackServerAppConstructorOptions<HasTokenStore, ProjectId>\n & {\n superSecretAdminKey?: string,\n projectOwnerSession?: InternalSession | (() => Promise<string | null>),\n }\n);\n\n\n/** @deprecated Use `HexclaveAdminApp` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */\nexport type StackAdminApp<HasTokenStore extends boolean = boolean, ProjectId extends string = string> = (\n & AsyncStoreProperty<\"project\", [], AdminProject, false>\n & AsyncStoreProperty<\"internalApiKeys\", [], InternalApiKey[], true>\n & AsyncStoreProperty<\"teamPermissionDefinitions\", [], AdminTeamPermissionDefinition[], true>\n & AsyncStoreProperty<\"projectPermissionDefinitions\", [], AdminProjectPermissionDefinition[], true>\n & AsyncStoreProperty<\"emailThemes\", [], { id: string, displayName: string }[], true>\n & AsyncStoreProperty<\"emailPreview\", [{ themeId?: string | null | false, themeTsxSource?: string, templateId?: string, templateTsxSource?: string }], string, false>\n & AsyncStoreProperty<\"emailPreviewWithEditableMarkers\", [{ themeId?: string | null | false, themeTsxSource?: string, templateId?: string, templateTsxSource?: string, editableSource?: 'template' | 'theme' | 'both' }], { html: string, editableRegions?: Record<string, unknown> }, false> // THIS_LINE_PLATFORM react-like\n & AsyncStoreProperty<\"emailTemplates\", [], { id: string, displayName: string, themeId?: string, tsxSource: string }[], true>\n & AsyncStoreProperty<\"emailDrafts\", [], { id: string, displayName: string, themeId: string | undefined | false, tsxSource: string, sentAt: Date | null }[], true>\n & AsyncStoreProperty<\"stripeAccountInfo\", [], { account_id: string, charges_enabled: boolean, details_submitted: boolean, payouts_enabled: boolean } | null, false>\n & AsyncStoreProperty<\n \"transactions\",\n [{\n cursor?: string,\n limit?: number,\n type?: TransactionType,\n customerType?: 'user' | 'team' | 'custom',\n customerId?: string,\n }],\n { transactions: Transaction[], nextCursor: string | null },\n true\n >\n & {\n createInternalApiKey(options: InternalApiKeyCreateOptions): Promise<InternalApiKeyFirstView>,\n\n createTeamPermissionDefinition(data: AdminTeamPermissionDefinitionCreateOptions): Promise<AdminTeamPermission>,\n updateTeamPermissionDefinition(permissionId: string, data: AdminTeamPermissionDefinitionUpdateOptions): Promise<void>,\n deleteTeamPermissionDefinition(permissionId: string): Promise<void>,\n /**\n * @param options.query Free-text search; matches against permission ID and description.\n */\n listTeamPermissionDefinitionsPaginated(options: { limit: number, cursor?: string, query?: string }): Promise<{ items: AdminTeamPermissionDefinition[], nextCursor: string | null }>,\n\n createProjectPermissionDefinition(data: AdminProjectPermissionDefinitionCreateOptions): Promise<AdminProjectPermission>,\n updateProjectPermissionDefinition(permissionId: string, data: AdminProjectPermissionDefinitionUpdateOptions): Promise<void>,\n deleteProjectPermissionDefinition(permissionId: string): Promise<void>,\n\n useSvixToken(): { token: string, url: string | undefined }, // THIS_LINE_PLATFORM react-like\n\n sendTestEmail(options: {\n recipientEmail: string,\n emailConfig: EmailConfig,\n }): Promise<Result<undefined, { errorMessage: string }>>,\n\n sendTestWebhook(options: { endpointId: string }): Promise<Result<undefined, { errorMessage: string }>>,\n\n sendSignInInvitationEmail(email: string, callbackUrl: string): Promise<void>,\n\n listSentEmails(): Promise<AdminSentEmail[]>,\n setupManagedEmailProvider(options: { subdomain: string, senderLocalPart: string }): Promise<ManagedEmailProviderSetupResult>,\n checkManagedEmailStatus(options: { domainId: string, subdomain: string, senderLocalPart: string }): Promise<ManagedEmailProviderStatus>,\n listManagedEmailDomains(): Promise<ManagedEmailProviderListItem[]>,\n applyManagedEmailProvider(options: { domainId: string }): Promise<{ status: \"applied\" }>,\n deleteManagedEmailDomain(options: { resendDomainId: string }): Promise<{ status: \"deleted\" }>,\n\n useEmailTheme(id: string): { displayName: string, tsxSource: string }, // THIS_LINE_PLATFORM react-like\n createEmailTheme(displayName: string): Promise<{ id: string }>,\n updateEmailTheme(id: string, tsxSource: string): Promise<void>,\n deleteEmailTheme(id: string): Promise<void>,\n saveChatMessage(threadId: string, message: any): Promise<void>,\n listChatMessages(threadId: string): Promise<{ messages: Array<any> }>,\n rewriteTemplateSourceWithAI(templateTsxSource: string): Promise<{ tsxSource: string }>,\n updateEmailTemplate(id: string, tsxSource: string, themeId: string | null | false): Promise<{ renderedHtml: string }>,\n createEmailTemplate(displayName: string): Promise<{ id: string }>,\n deleteEmailTemplate(id: string): Promise<void>,\n\n setupPayments(): Promise<{ url: string }>,\n createStripeWidgetAccountSession(): Promise<{ client_secret: string }>,\n getPaymentMethodConfigs(): Promise<{ configId: string, methods: Array<{ id: string, name: string, enabled: boolean, available: boolean, overridable: boolean }> } | null>,\n updatePaymentMethodConfigs(configId: string, updates: Record<string, 'on' | 'off'>): Promise<void>,\n createEmailDraft(options: { displayName: string, themeId?: string | undefined | false, tsxSource?: string }): Promise<{ id: string }>,\n updateEmailDraft(id: string, data: { displayName?: string, themeId?: string | undefined | false, tsxSource?: string }): Promise<void>,\n deleteEmailDraft(id: string): Promise<void>,\n refreshEmailDrafts(): Promise<void>,\n createItemQuantityChange(options: (\n { userId: string, itemId: string, quantity: number, expiresAt?: string, description?: string } |\n { teamId: string, itemId: string, quantity: number, expiresAt?: string, description?: string } |\n { customCustomerId: string, itemId: string, quantity: number, expiresAt?: string, description?: string }\n )): Promise<void>,\n refundTransaction(options: {\n type: \"subscription\" | \"one-time-purchase\",\n id: string,\n invoiceId?: string,\n amountUsd: MoneyAmount,\n endAction?: \"now\" | \"at-period-end\",\n }): Promise<{ refundTransactionId: string }>,\n queryAnalytics(options: AnalyticsQueryOptions): Promise<AnalyticsQueryResponse>,\n getAnalyticsClickmap(options: AnalyticsClickmapOptions): Promise<AnalyticsClickmapResponse>,\n createAnalyticsClickmapToken(options: { origin: string }): Promise<AnalyticsClickmapTokenResponse>,\n\n listSessionReplays(options?: ListSessionReplaysOptions): Promise<ListSessionReplaysResult>,\n getSessionReplay(sessionReplayId: string): Promise<AdminSessionReplay>,\n listSessionReplayChunks(sessionReplayId: string, options?: ListSessionReplayChunksOptions): Promise<ListSessionReplayChunksResult>,\n getSessionReplayChunkEvents(sessionReplayId: string, chunkId: string): Promise<AdminGetSessionReplayChunkEventsResponse>,\n getSessionReplayEvents(sessionReplayId: string, options?: { offset?: number, limit?: number }): Promise<SessionReplayAllEventsResult>,\n\n // Email Outbox methods\n listOutboxEmails(options?: EmailOutboxListOptions): Promise<EmailOutboxListResult>,\n getOutboxEmail(id: string): Promise<AdminEmailOutbox>,\n updateOutboxEmail(id: string, options: EmailOutboxUpdateOptions): Promise<AdminEmailOutbox>,\n pauseOutboxEmail(id: string): Promise<AdminEmailOutbox>,\n unpauseOutboxEmail(id: string): Promise<AdminEmailOutbox>,\n cancelOutboxEmail(id: string): Promise<AdminEmailOutbox>,\n }\n & StackServerApp<HasTokenStore, ProjectId>\n);\n/** @deprecated Use `HexclaveAdminAppConstructor` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */\nexport type StackAdminAppConstructor = {\n new <\n HasTokenStore extends boolean,\n ProjectId extends string\n >(options: StackAdminAppConstructorOptions<HasTokenStore, ProjectId>): StackAdminApp<HasTokenStore, ProjectId>,\n new(options: StackAdminAppConstructorOptions<boolean, string>): StackAdminApp<boolean, string>,\n};\nexport type HexclaveAdminAppConstructorOptions<HasTokenStore extends boolean, ProjectId extends string> = StackAdminAppConstructorOptions<HasTokenStore, ProjectId>;\nexport type HexclaveAdminApp<HasTokenStore extends boolean = boolean, ProjectId extends string = string> = StackAdminApp<HasTokenStore, ProjectId>;\nexport type HexclaveAdminAppConstructor = StackAdminAppConstructor;\nexport const HexclaveAdminApp: HexclaveAdminAppConstructor = _HexclaveAdminAppImpl;\n/** @deprecated Use `HexclaveAdminApp` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */\nexport const StackAdminApp: StackAdminAppConstructor = HexclaveAdminApp;\n"],"mappings":";;;;;AAgMA,MAAa,mBAAgDA;;AAE7D,MAAa,gBAA0C"}
|
|
1
|
+
{"version":3,"file":"admin-app.js","names":["_HexclaveAdminAppImpl"],"sources":["../../../../../src/lib/hexclave-app/apps/interfaces/admin-app.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport type { AnalyticsClickmapOptions, AnalyticsClickmapResponse, AnalyticsClickmapTokenResponse } from \"@hexclave/shared/dist/interface/admin-metrics\";\nimport { AnalyticsQueryOptions, AnalyticsQueryResponse } from \"@hexclave/shared/dist/interface/crud/analytics\";\nimport type { AdminGetSessionReplayChunkEventsResponse, AdminGetSessionReplayAllEventsResponse } from \"@hexclave/shared/dist/interface/crud/session-replays\";\nimport type { Transaction, TransactionType } from \"@hexclave/shared/dist/interface/crud/transactions\";\nimport { InternalSession } from \"@hexclave/shared/dist/sessions\";\nimport type { MoneyAmount } from \"@hexclave/shared/dist/utils/currency-constants\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport { AsyncStoreProperty, EmailConfig } from \"../../common\";\nimport { AdminEmailOutbox, AdminSentEmail } from \"../../email\";\nimport { InternalApiKey, InternalApiKeyCreateOptions, InternalApiKeyFirstView } from \"../../internal-api-keys\";\nimport { AdminProjectPermission, AdminProjectPermissionDefinition, AdminProjectPermissionDefinitionCreateOptions, AdminProjectPermissionDefinitionUpdateOptions, AdminTeamPermission, AdminTeamPermissionDefinition, AdminTeamPermissionDefinitionCreateOptions, AdminTeamPermissionDefinitionUpdateOptions } from \"../../permissions\";\nimport { AdminProject } from \"../../projects\";\nimport { _HexclaveAdminAppImpl } from \"../implementations\";\nimport { StackServerApp, StackServerAppConstructorOptions } from \"./server-app\";\n\nexport type EmailOutboxListOptions = {\n status?: string,\n simpleStatus?: string,\n userId?: string,\n limit?: number,\n cursor?: string,\n};\n\nexport type EmailOutboxListResult = {\n items: AdminEmailOutbox[],\n nextCursor: string | null,\n};\n\nexport type EmailOutboxUpdateOptions = {\n isPaused?: boolean,\n scheduledAtMillis?: number,\n cancel?: boolean,\n tsxSource?: string,\n themeId?: string | null,\n};\n\nexport type ManagedEmailProviderSetupResult = {\n domainId: string,\n subdomain: string,\n senderLocalPart: string,\n nameServerRecords: string[],\n status: ManagedEmailProviderStatus[\"status\"],\n};\n\nexport type ManagedEmailProviderStatus = {\n status: \"pending_dns\" | \"pending_verification\" | \"verified\" | \"applied\" | \"failed\",\n};\n\nexport type ManagedEmailProviderListItem = {\n domainId: string,\n subdomain: string,\n senderLocalPart: string,\n status: ManagedEmailProviderStatus[\"status\"],\n nameServerRecords: string[],\n};\n\nimport type { AdminSessionReplay, ListSessionReplayChunksOptions, ListSessionReplayChunksResult, ListSessionReplaysOptions, ListSessionReplaysResult, SessionReplayAllEventsResult } from \"../../session-replays\";\nexport type { AdminSessionReplay, AdminSessionReplayChunk, ListSessionReplaysOptions, ListSessionReplaysResult, ListSessionReplayChunksOptions, ListSessionReplayChunksResult, SessionReplayAllEventsResult } from \"../../session-replays\";\n\n\n/** @deprecated Use `HexclaveAdminAppConstructorOptions` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */\nexport type StackAdminAppConstructorOptions<HasTokenStore extends boolean, ProjectId extends string> = (\n & StackServerAppConstructorOptions<HasTokenStore, ProjectId>\n & {\n superSecretAdminKey?: string,\n projectOwnerSession?: InternalSession | (() => Promise<string | null>),\n }\n);\n\n\n/** @deprecated Use `HexclaveAdminApp` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */\nexport type StackAdminApp<HasTokenStore extends boolean = boolean, ProjectId extends string = string> = (\n & AsyncStoreProperty<\"project\", [], AdminProject, false>\n & AsyncStoreProperty<\"internalApiKeys\", [], InternalApiKey[], true>\n & AsyncStoreProperty<\"teamPermissionDefinitions\", [], AdminTeamPermissionDefinition[], true>\n & AsyncStoreProperty<\"projectPermissionDefinitions\", [], AdminProjectPermissionDefinition[], true>\n & AsyncStoreProperty<\"emailThemes\", [], { id: string, displayName: string }[], true>\n & AsyncStoreProperty<\"emailPreview\", [{ themeId?: string | null | false, themeTsxSource?: string, templateId?: string, templateTsxSource?: string }], string, false>\n & AsyncStoreProperty<\"emailPreviewWithEditableMarkers\", [{ themeId?: string | null | false, themeTsxSource?: string, templateId?: string, templateTsxSource?: string, editableSource?: 'template' | 'theme' | 'both' }], { html: string, editableRegions?: Record<string, unknown> }, false> // THIS_LINE_PLATFORM react-like\n & AsyncStoreProperty<\"emailTemplates\", [], { id: string, displayName: string, themeId?: string, tsxSource: string }[], true>\n & AsyncStoreProperty<\"emailDrafts\", [], { id: string, displayName: string, themeId: string | undefined | false, tsxSource: string, sentAt: Date | null }[], true>\n & AsyncStoreProperty<\"stripeAccountInfo\", [], { account_id: string, charges_enabled: boolean, details_submitted: boolean, payouts_enabled: boolean } | null, false>\n & AsyncStoreProperty<\n \"transactions\",\n [{\n cursor?: string,\n limit?: number,\n type?: TransactionType,\n customerType?: 'user' | 'team' | 'custom',\n customerId?: string,\n }],\n { transactions: Transaction[], nextCursor: string | null },\n true\n >\n & {\n createInternalApiKey(options: InternalApiKeyCreateOptions): Promise<InternalApiKeyFirstView>,\n\n createTeamPermissionDefinition(data: AdminTeamPermissionDefinitionCreateOptions): Promise<AdminTeamPermission>,\n updateTeamPermissionDefinition(permissionId: string, data: AdminTeamPermissionDefinitionUpdateOptions): Promise<void>,\n deleteTeamPermissionDefinition(permissionId: string): Promise<void>,\n /**\n * @param options.query Free-text search; matches against permission ID and description.\n */\n listTeamPermissionDefinitionsPaginated(options: { limit: number, cursor?: string, query?: string }): Promise<{ items: AdminTeamPermissionDefinition[], nextCursor: string | null }>,\n\n createProjectPermissionDefinition(data: AdminProjectPermissionDefinitionCreateOptions): Promise<AdminProjectPermission>,\n updateProjectPermissionDefinition(permissionId: string, data: AdminProjectPermissionDefinitionUpdateOptions): Promise<void>,\n deleteProjectPermissionDefinition(permissionId: string): Promise<void>,\n\n useSvixToken(): { token: string, url: string | undefined }, // THIS_LINE_PLATFORM react-like\n\n sendTestEmail(options: {\n recipientEmail: string,\n emailConfig: EmailConfig,\n }): Promise<Result<undefined, { errorMessage: string }>>,\n\n sendTestWebhook(options: { endpointId: string }): Promise<Result<undefined, { errorMessage: string }>>,\n\n sendSignInInvitationEmail(email: string, callbackUrl: string): Promise<void>,\n\n listSentEmails(): Promise<AdminSentEmail[]>,\n setupManagedEmailProvider(options: { subdomain: string, senderLocalPart: string }): Promise<ManagedEmailProviderSetupResult>,\n checkManagedEmailStatus(options: { domainId: string, subdomain: string, senderLocalPart: string }): Promise<ManagedEmailProviderStatus>,\n listManagedEmailDomains(): Promise<ManagedEmailProviderListItem[]>,\n applyManagedEmailProvider(options: { domainId: string }): Promise<{ status: \"applied\" }>,\n deleteManagedEmailDomain(options: { resendDomainId: string }): Promise<{ status: \"deleted\" }>,\n\n useEmailTheme(id: string): { displayName: string, tsxSource: string }, // THIS_LINE_PLATFORM react-like\n createEmailTheme(displayName: string): Promise<{ id: string }>,\n updateEmailTheme(id: string, tsxSource: string): Promise<void>,\n deleteEmailTheme(id: string): Promise<void>,\n saveChatMessage(threadId: string, message: any): Promise<void>,\n listChatMessages(threadId: string): Promise<{ messages: Array<any> }>,\n rewriteTemplateSourceWithAI(templateTsxSource: string): Promise<{ tsxSource: string }>,\n updateEmailTemplate(id: string, tsxSource: string, themeId: string | null | false): Promise<{ renderedHtml: string }>,\n createEmailTemplate(displayName: string): Promise<{ id: string }>,\n deleteEmailTemplate(id: string): Promise<void>,\n\n setupPayments(): Promise<{ url: string }>,\n createStripeWidgetAccountSession(): Promise<{ client_secret: string }>,\n getPaymentMethodConfigs(): Promise<{ configId: string, methods: Array<{ id: string, name: string, enabled: boolean, available: boolean, overridable: boolean }> } | null>,\n updatePaymentMethodConfigs(configId: string, updates: Record<string, 'on' | 'off'>): Promise<void>,\n createEmailDraft(options: { displayName: string, themeId?: string | undefined | false, tsxSource?: string }): Promise<{ id: string }>,\n updateEmailDraft(id: string, data: { displayName?: string, themeId?: string | undefined | false, tsxSource?: string }): Promise<void>,\n deleteEmailDraft(id: string): Promise<void>,\n refreshEmailDrafts(): Promise<void>,\n createItemQuantityChange(options: (\n { userId: string, itemId: string, quantity: number, expiresAt?: string, description?: string } |\n { teamId: string, itemId: string, quantity: number, expiresAt?: string, description?: string } |\n { customCustomerId: string, itemId: string, quantity: number, expiresAt?: string, description?: string }\n )): Promise<void>,\n refundTransaction(options: {\n type: \"subscription\" | \"one-time-purchase\",\n id: string,\n invoiceId?: string,\n amountUsd: MoneyAmount,\n endAction?: \"now\" | \"at-period-end\",\n }): Promise<{ refundTransactionId: string }>,\n queryAnalytics(options: AnalyticsQueryOptions): Promise<AnalyticsQueryResponse>,\n getAnalyticsClickmap(options: AnalyticsClickmapOptions): Promise<AnalyticsClickmapResponse>,\n createAnalyticsClickmapToken(options: { origin: string }): Promise<AnalyticsClickmapTokenResponse>,\n\n listSessionReplays(options?: ListSessionReplaysOptions): Promise<ListSessionReplaysResult>,\n getSessionReplay(sessionReplayId: string): Promise<AdminSessionReplay>,\n listSessionReplayChunks(sessionReplayId: string, options?: ListSessionReplayChunksOptions): Promise<ListSessionReplayChunksResult>,\n getSessionReplayChunkEvents(sessionReplayId: string, chunkId: string): Promise<AdminGetSessionReplayChunkEventsResponse>,\n getSessionReplayEvents(sessionReplayId: string, options?: { offset?: number, limit?: number }): Promise<SessionReplayAllEventsResult>,\n\n // Email Outbox methods\n listOutboxEmails(options?: EmailOutboxListOptions): Promise<EmailOutboxListResult>,\n getOutboxEmail(id: string): Promise<AdminEmailOutbox>,\n updateOutboxEmail(id: string, options: EmailOutboxUpdateOptions): Promise<AdminEmailOutbox>,\n pauseOutboxEmail(id: string): Promise<AdminEmailOutbox>,\n unpauseOutboxEmail(id: string): Promise<AdminEmailOutbox>,\n cancelOutboxEmail(id: string): Promise<AdminEmailOutbox>,\n }\n & StackServerApp<HasTokenStore, ProjectId>\n);\n/** @deprecated Use `HexclaveAdminAppConstructor` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */\nexport type StackAdminAppConstructor = {\n new <\n HasTokenStore extends boolean,\n ProjectId extends string\n >(options: StackAdminAppConstructorOptions<HasTokenStore, ProjectId>): StackAdminApp<HasTokenStore, ProjectId>,\n new(options: StackAdminAppConstructorOptions<boolean, string>): StackAdminApp<boolean, string>,\n};\nexport type HexclaveAdminAppConstructorOptions<HasTokenStore extends boolean, ProjectId extends string> = StackAdminAppConstructorOptions<HasTokenStore, ProjectId>;\nexport type HexclaveAdminApp<HasTokenStore extends boolean = boolean, ProjectId extends string = string> = StackAdminApp<HasTokenStore, ProjectId>;\nexport type HexclaveAdminAppConstructor = StackAdminAppConstructor;\nexport const HexclaveAdminApp: HexclaveAdminAppConstructor = _HexclaveAdminAppImpl;\n/** @deprecated Use `HexclaveAdminApp` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */\nexport const StackAdminApp: StackAdminAppConstructor = HexclaveAdminApp;\n"],"mappings":";;;;;AAiMA,MAAa,mBAAgDA;;AAE7D,MAAa,gBAA0C"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
|
|
3
3
|
"name": "@hexclave/next",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.22",
|
|
5
5
|
"repository": "https://github.com/hexclave/hexclave",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -75,9 +75,9 @@
|
|
|
75
75
|
"rrweb": "^1.1.3",
|
|
76
76
|
"tsx": "^4.21.0",
|
|
77
77
|
"yup": "^1.7.1",
|
|
78
|
-
"@hexclave/sc": "1.0.
|
|
79
|
-
"@hexclave/
|
|
80
|
-
"@hexclave/
|
|
78
|
+
"@hexclave/sc": "1.0.22",
|
|
79
|
+
"@hexclave/ui": "1.0.22",
|
|
80
|
+
"@hexclave/shared": "1.0.22"
|
|
81
81
|
},
|
|
82
82
|
"peerDependencies": {
|
|
83
83
|
"@types/react": ">=18.0.0",
|
|
@@ -0,0 +1,109 @@
|
|
|
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
|
+
// @vitest-environment jsdom
|
|
6
|
+
|
|
7
|
+
import { KnownErrors } from "@hexclave/shared";
|
|
8
|
+
import React, { act } from "react";
|
|
9
|
+
import { createRoot, type Root } from "react-dom/client";
|
|
10
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
11
|
+
import type { StackClientApp } from "../lib/hexclave-app/apps/interfaces/client-app";
|
|
12
|
+
import { TranslationProviderClient } from "../providers/translation-provider-client";
|
|
13
|
+
import { OAuthCallback } from "./oauth-callback";
|
|
14
|
+
|
|
15
|
+
const appMockState = vi.hoisted(() => ({ app: null as unknown }));
|
|
16
|
+
|
|
17
|
+
vi.mock("..", () => ({
|
|
18
|
+
useStackApp: () => {
|
|
19
|
+
if (appMockState.app == null) {
|
|
20
|
+
throw new Error("Expected test app to be set before rendering.");
|
|
21
|
+
}
|
|
22
|
+
return appMockState.app;
|
|
23
|
+
},
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
vi.mock("@hexclave/ui", async () => {
|
|
27
|
+
const React = await import("react");
|
|
28
|
+
return {
|
|
29
|
+
Button: (props: { children: React.ReactNode, onClick?: () => void }) => (
|
|
30
|
+
<button type="button" onClick={props.onClick}>{props.children}</button>
|
|
31
|
+
),
|
|
32
|
+
Spinner: () => <div data-testid="spinner" />,
|
|
33
|
+
Typography: (props: { children: React.ReactNode }) => <div>{props.children}</div>,
|
|
34
|
+
cn: (...classes: (string | false | null | undefined)[]) => classes.filter(Boolean).join(" "),
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const previousActEnvironment = Reflect.get(globalThis, "IS_REACT_ACT_ENVIRONMENT");
|
|
39
|
+
|
|
40
|
+
function createAppTestDouble(options: {
|
|
41
|
+
callOAuthCallback: () => Promise<boolean>,
|
|
42
|
+
}) {
|
|
43
|
+
const app = {
|
|
44
|
+
callOAuthCallback: options.callOAuthCallback,
|
|
45
|
+
redirectToSignIn: vi.fn(async () => {}),
|
|
46
|
+
redirectToHome: vi.fn(async () => {}),
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// This test double intentionally implements only the StackClientApp surface
|
|
50
|
+
// that OAuthCallback and the rendered error card touch.
|
|
51
|
+
return app as unknown as StackClientApp<true>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let root: Root | null = null;
|
|
55
|
+
let container: HTMLDivElement | null = null;
|
|
56
|
+
|
|
57
|
+
async function renderWithApp(app: StackClientApp<true>) {
|
|
58
|
+
appMockState.app = app;
|
|
59
|
+
container = document.createElement("div");
|
|
60
|
+
document.body.append(container);
|
|
61
|
+
root = createRoot(container);
|
|
62
|
+
await act(async () => {
|
|
63
|
+
root?.render(
|
|
64
|
+
<TranslationProviderClient quetzalKeys={new Map()} quetzalLocale={new Map()}>
|
|
65
|
+
<OAuthCallback />
|
|
66
|
+
</TranslationProviderClient>
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function flushEffects() {
|
|
72
|
+
await act(async () => {
|
|
73
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
describe("OAuthCallback", () => {
|
|
78
|
+
beforeEach(() => {
|
|
79
|
+
Reflect.set(globalThis, "IS_REACT_ACT_ENVIRONMENT", true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
afterEach(() => {
|
|
83
|
+
act(() => {
|
|
84
|
+
root?.unmount();
|
|
85
|
+
});
|
|
86
|
+
container?.remove();
|
|
87
|
+
root = null;
|
|
88
|
+
container = null;
|
|
89
|
+
appMockState.app = null;
|
|
90
|
+
vi.restoreAllMocks();
|
|
91
|
+
Reflect.set(globalThis, "IS_REACT_ACT_ENVIRONMENT", previousActEnvironment);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("renders backend-encoded OAuth callback errors on the callback page", async () => {
|
|
95
|
+
const errorMessage = "Your sign up was rejected by an administrator's sign-up rule.";
|
|
96
|
+
const callOAuthCallback = vi.fn(async () => {
|
|
97
|
+
throw new KnownErrors.SignUpRejected(errorMessage);
|
|
98
|
+
});
|
|
99
|
+
const app = createAppTestDouble({ callOAuthCallback });
|
|
100
|
+
|
|
101
|
+
await renderWithApp(app);
|
|
102
|
+
await flushEffects();
|
|
103
|
+
|
|
104
|
+
expect(callOAuthCallback).toHaveBeenCalledOnce();
|
|
105
|
+
expect(container?.textContent).toContain("SIGN_UP_REJECTED");
|
|
106
|
+
expect(container?.textContent).toContain(errorMessage);
|
|
107
|
+
expect(app.redirectToSignIn).not.toHaveBeenCalled();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -13,27 +13,19 @@ import { useEffect, useRef, useState } from "react";
|
|
|
13
13
|
import { useStackApp } from "..";
|
|
14
14
|
import { MaybeFullPage } from "../components/elements/maybe-full-page";
|
|
15
15
|
import { StyledLink } from "../components/link";
|
|
16
|
-
import { hexclaveAppInternalsSymbol } from "../lib/hexclave-app";
|
|
17
16
|
import { useTranslation } from "../lib/translations";
|
|
17
|
+
import { ErrorPage } from "./error-page";
|
|
18
18
|
|
|
19
19
|
export function OAuthCallback({ fullPage }: { fullPage?: boolean }) {
|
|
20
20
|
const { t } = useTranslation();
|
|
21
21
|
const app = useStackApp();
|
|
22
22
|
const called = useRef(false);
|
|
23
23
|
const [showRedirectLink, setShowRedirectLink] = useState(false);
|
|
24
|
-
const [
|
|
24
|
+
const [errorSearchParams, setErrorSearchParams] = useState<Record<string, string> | null>(null);
|
|
25
25
|
|
|
26
26
|
useEffect(() => runAsynchronously(async () => {
|
|
27
27
|
if (called.current) return;
|
|
28
28
|
called.current = true;
|
|
29
|
-
const redirectToError = async (url: URL) => {
|
|
30
|
-
const urlString = url.toString();
|
|
31
|
-
if (app[hexclaveAppInternalsSymbol].getRedirectMethod() === "none") {
|
|
32
|
-
setRedirectUrl(urlString);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
await app[hexclaveAppInternalsSymbol].redirectToUrl(urlString, { replace: true });
|
|
36
|
-
};
|
|
37
29
|
try {
|
|
38
30
|
const hasRedirected = await app.callOAuthCallback();
|
|
39
31
|
if (!hasRedirected) {
|
|
@@ -41,15 +33,15 @@ export function OAuthCallback({ fullPage }: { fullPage?: boolean }) {
|
|
|
41
33
|
}
|
|
42
34
|
} catch (e) {
|
|
43
35
|
if (KnownError.isKnownError(e)) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
36
|
+
setErrorSearchParams({
|
|
37
|
+
errorCode: e.errorCode,
|
|
38
|
+
message: e.message,
|
|
39
|
+
details: JSON.stringify(e.details ?? {}),
|
|
40
|
+
});
|
|
49
41
|
return;
|
|
50
42
|
}
|
|
51
43
|
captureError("<OAuthCallback />", e);
|
|
52
|
-
|
|
44
|
+
setErrorSearchParams({});
|
|
53
45
|
}
|
|
54
46
|
}), [app]);
|
|
55
47
|
|
|
@@ -57,6 +49,10 @@ export function OAuthCallback({ fullPage }: { fullPage?: boolean }) {
|
|
|
57
49
|
setTimeout(() => setShowRedirectLink(true), 3000);
|
|
58
50
|
}, []);
|
|
59
51
|
|
|
52
|
+
if (errorSearchParams != null) {
|
|
53
|
+
return <ErrorPage searchParams={errorSearchParams} fullPage={fullPage} />;
|
|
54
|
+
}
|
|
55
|
+
|
|
60
56
|
return (
|
|
61
57
|
<MaybeFullPage
|
|
62
58
|
fullPage={fullPage ?? false}
|
|
@@ -71,11 +67,10 @@ export function OAuthCallback({ fullPage }: { fullPage?: boolean }) {
|
|
|
71
67
|
<div className="flex flex-col justify-center items-center gap-4">
|
|
72
68
|
<Spinner size={20} />
|
|
73
69
|
</div>
|
|
74
|
-
{showRedirectLink
|
|
70
|
+
{showRedirectLink ? <p>{t('If you are not redirected automatically, ')}<StyledLink
|
|
75
71
|
className="whitespace-nowrap"
|
|
76
|
-
href=
|
|
72
|
+
href="#"
|
|
77
73
|
onClick={(e) => {
|
|
78
|
-
if (redirectUrl != null) return;
|
|
79
74
|
e.preventDefault();
|
|
80
75
|
runAsynchronously(app.redirectToHome());
|
|
81
76
|
}}
|
package/src/lib/auth.test.ts
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
//===========================================
|
|
5
5
|
// @vitest-environment jsdom
|
|
6
6
|
|
|
7
|
-
import { HexclaveClientInterface } from "@hexclave/shared";
|
|
7
|
+
import { HexclaveClientInterface, KnownErrors } from "@hexclave/shared";
|
|
8
8
|
import { describe, expect, it, vi } from "vitest";
|
|
9
|
-
import { getNewOAuthProviderOrScopeUrl } from "./auth";
|
|
9
|
+
import { callOAuthCallback, getNewOAuthProviderOrScopeUrl } from "./auth";
|
|
10
10
|
|
|
11
11
|
vi.mock("./cookie", async (importOriginal) => {
|
|
12
12
|
const actual = await importOriginal<typeof import("./cookie")>();
|
|
@@ -19,18 +19,22 @@ vi.mock("./cookie", async (importOriginal) => {
|
|
|
19
19
|
};
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
+
function createTestInterface() {
|
|
23
|
+
return new HexclaveClientInterface({
|
|
24
|
+
clientVersion: "test",
|
|
25
|
+
getBaseUrl: () => "https://api.example.com",
|
|
26
|
+
getApiUrls: () => ["https://api.example.com"],
|
|
27
|
+
extraRequestHeaders: {},
|
|
28
|
+
projectId: "00000000-0000-4000-8000-000000000000",
|
|
29
|
+
publishableClientKey: "pck_test",
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
22
33
|
describe("getNewOAuthProviderOrScopeUrl", () => {
|
|
23
34
|
it("returns the OAuth URL without performing navigation", async () => {
|
|
24
35
|
window.history.replaceState({}, "", "/account?after_auth_return_to=%2Fsettings");
|
|
25
36
|
|
|
26
|
-
const iface =
|
|
27
|
-
clientVersion: "test",
|
|
28
|
-
getBaseUrl: () => "https://api.example.com",
|
|
29
|
-
getApiUrls: () => ["https://api.example.com"],
|
|
30
|
-
extraRequestHeaders: {},
|
|
31
|
-
projectId: "00000000-0000-4000-8000-000000000000",
|
|
32
|
-
publishableClientKey: "pck_test",
|
|
33
|
-
});
|
|
37
|
+
const iface = createTestInterface();
|
|
34
38
|
const session = iface.createSession({ refreshToken: null, accessToken: null });
|
|
35
39
|
|
|
36
40
|
const location = await getNewOAuthProviderOrScopeUrl(
|
|
@@ -65,3 +69,21 @@ describe("getNewOAuthProviderOrScopeUrl", () => {
|
|
|
65
69
|
`);
|
|
66
70
|
});
|
|
67
71
|
});
|
|
72
|
+
|
|
73
|
+
describe("callOAuthCallback", () => {
|
|
74
|
+
it("turns provider access denial callback params into a known error", async () => {
|
|
75
|
+
window.history.replaceState({}, "", "/handler/oauth-callback?error=access_denied&error_description=User+cancelled");
|
|
76
|
+
|
|
77
|
+
await expect(callOAuthCallback(createTestInterface(), "/handler/oauth-callback"))
|
|
78
|
+
.rejects.toSatisfy((error: unknown) => KnownErrors.OAuthProviderAccessDenied.isInstance(error));
|
|
79
|
+
expect(window.location.href).toBe("http://localhost:3000/handler/oauth-callback");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("turns generic provider error callback params into a known error", async () => {
|
|
83
|
+
window.history.replaceState({}, "", "/handler/oauth-callback?error=server_error&error_description=Provider+failed");
|
|
84
|
+
|
|
85
|
+
await expect(callOAuthCallback(createTestInterface(), "/handler/oauth-callback"))
|
|
86
|
+
.rejects.toSatisfy((error: unknown) => KnownErrors.OAuthProviderTemporarilyUnavailable.isInstance(error));
|
|
87
|
+
expect(window.location.href).toBe("http://localhost:3000/handler/oauth-callback");
|
|
88
|
+
});
|
|
89
|
+
});
|
package/src/lib/auth.ts
CHANGED
|
@@ -2,7 +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 { KnownError, HexclaveClientInterface } from "@hexclave/shared";
|
|
5
|
+
import { KnownError, KnownErrors, HexclaveClientInterface } from "@hexclave/shared";
|
|
6
6
|
import { InternalSession } from "@hexclave/shared/dist/sessions";
|
|
7
7
|
import { HexclaveAssertionError, throwErr } from "@hexclave/shared/dist/utils/errors";
|
|
8
8
|
import { Result } from "@hexclave/shared/dist/utils/results";
|
|
@@ -50,10 +50,39 @@ type OAuthCallbackConsumptionResult =
|
|
|
50
50
|
error: KnownError,
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
+
const oauthErrorParams = ["error", "error_description", "errorCode", "message", "details"] as const;
|
|
54
|
+
|
|
55
|
+
function removeOAuthErrorParamsFromHistory(originalUrl: URL): void {
|
|
56
|
+
const newUrl = new URL(originalUrl);
|
|
57
|
+
for (const param of oauthErrorParams) {
|
|
58
|
+
newUrl.searchParams.delete(param);
|
|
59
|
+
}
|
|
60
|
+
window.history.replaceState({}, "", newUrl.toString());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getProviderOAuthErrorFromUrl(originalUrl: URL): KnownError | null {
|
|
64
|
+
const providerError = originalUrl.searchParams.get("error");
|
|
65
|
+
const providerErrorDescription = originalUrl.searchParams.get("error_description");
|
|
66
|
+
if (providerError == null && providerErrorDescription == null) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
switch (providerError) {
|
|
71
|
+
case "access_denied":
|
|
72
|
+
case "consent_required": {
|
|
73
|
+
return new KnownErrors.OAuthProviderAccessDenied();
|
|
74
|
+
}
|
|
75
|
+
case "server_error":
|
|
76
|
+
case "temporarily_unavailable":
|
|
77
|
+
default: {
|
|
78
|
+
return new KnownErrors.OAuthProviderTemporarilyUnavailable();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
53
83
|
function consumeOAuthCallbackQueryParams(options?: {
|
|
54
84
|
dontWarnAboutMissingQueryParams?: boolean,
|
|
55
85
|
}): OAuthCallbackConsumptionResult | null {
|
|
56
|
-
const oauthErrorParams = ["error", "error_description", "errorCode", "message", "details"] as const;
|
|
57
86
|
const requiredParams = ["code", "state"];
|
|
58
87
|
const originalUrl = new URL(window.location.href);
|
|
59
88
|
const knownErrorCode = originalUrl.searchParams.get("errorCode");
|
|
@@ -72,11 +101,7 @@ function consumeOAuthCallbackQueryParams(options?: {
|
|
|
72
101
|
}
|
|
73
102
|
}
|
|
74
103
|
|
|
75
|
-
|
|
76
|
-
for (const param of oauthErrorParams) {
|
|
77
|
-
newUrl.searchParams.delete(param);
|
|
78
|
-
}
|
|
79
|
-
window.history.replaceState({}, "", newUrl.toString());
|
|
104
|
+
removeOAuthErrorParamsFromHistory(originalUrl);
|
|
80
105
|
|
|
81
106
|
return {
|
|
82
107
|
type: "known-error",
|
|
@@ -88,6 +113,15 @@ function consumeOAuthCallbackQueryParams(options?: {
|
|
|
88
113
|
};
|
|
89
114
|
}
|
|
90
115
|
|
|
116
|
+
const providerOAuthError = getProviderOAuthErrorFromUrl(originalUrl);
|
|
117
|
+
if (providerOAuthError != null && !requiredParams.every(param => originalUrl.searchParams.has(param))) {
|
|
118
|
+
removeOAuthErrorParamsFromHistory(originalUrl);
|
|
119
|
+
return {
|
|
120
|
+
type: "known-error",
|
|
121
|
+
error: providerOAuthError,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
91
125
|
for (const param of requiredParams) {
|
|
92
126
|
if (!originalUrl.searchParams.has(param)) {
|
|
93
127
|
if (!options?.dontWarnAboutMissingQueryParams) {
|
|
@@ -1092,10 +1092,11 @@ export class _HexclaveAdminAppImplIncomplete<HasTokenStore extends boolean, Proj
|
|
|
1092
1092
|
return result as AdminEmailOutbox;
|
|
1093
1093
|
}
|
|
1094
1094
|
|
|
1095
|
-
async listOutboxEmails(options?: { status?: string, simpleStatus?: string, limit?: number, cursor?: string }): Promise<{ items: AdminEmailOutbox[], nextCursor: string | null }> {
|
|
1095
|
+
async listOutboxEmails(options?: { status?: string, simpleStatus?: string, userId?: string, limit?: number, cursor?: string }): Promise<{ items: AdminEmailOutbox[], nextCursor: string | null }> {
|
|
1096
1096
|
const response = await this._interface.listOutboxEmails({
|
|
1097
1097
|
status: options?.status,
|
|
1098
1098
|
simple_status: options?.simpleStatus,
|
|
1099
|
+
user_id: options?.userId,
|
|
1099
1100
|
limit: options?.limit,
|
|
1100
1101
|
cursor: options?.cursor,
|
|
1101
1102
|
});
|
|
@@ -439,6 +439,72 @@ describe("StackClientApp cross-domain auth", () => {
|
|
|
439
439
|
}
|
|
440
440
|
});
|
|
441
441
|
|
|
442
|
+
it("redirects hosted current-page OAuth callback errors to the hosted error handler during startup", async () => {
|
|
443
|
+
const projectId = "00000000-0000-4000-8000-000000000010";
|
|
444
|
+
const previousWindow = globalThis.window;
|
|
445
|
+
const previousDocument = globalThis.document;
|
|
446
|
+
const callbackUrl = new URL("https://demo.stack-auth.com/dashboard");
|
|
447
|
+
callbackUrl.searchParams.set("errorCode", "SIGN_UP_REJECTED");
|
|
448
|
+
callbackUrl.searchParams.set("message", "Your sign up was rejected by an administrator's sign-up rule.");
|
|
449
|
+
callbackUrl.searchParams.set("details", JSON.stringify({
|
|
450
|
+
message: "Your sign up was rejected by an administrator's sign-up rule.",
|
|
451
|
+
}));
|
|
452
|
+
let currentHref = callbackUrl.toString();
|
|
453
|
+
let redirectedUrl = "";
|
|
454
|
+
const redirectSpy = vi.spyOn(StackClientApp.prototype as any, "_redirectTo").mockImplementation(async (...args: unknown[]) => {
|
|
455
|
+
const options = args[0] as { url: string | URL };
|
|
456
|
+
redirectedUrl = options.url.toString();
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
globalThis.document = createMockDocument();
|
|
460
|
+
globalThis.window = {
|
|
461
|
+
location: {
|
|
462
|
+
get href() {
|
|
463
|
+
return currentHref;
|
|
464
|
+
},
|
|
465
|
+
set href(value: string) {
|
|
466
|
+
currentHref = value;
|
|
467
|
+
},
|
|
468
|
+
origin: callbackUrl.origin,
|
|
469
|
+
},
|
|
470
|
+
history: {
|
|
471
|
+
replaceState: (_state: unknown, _title: string, url: string) => {
|
|
472
|
+
currentHref = new URL(url, currentHref).toString();
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
addEventListener: () => {},
|
|
476
|
+
removeEventListener: () => {},
|
|
477
|
+
} as any;
|
|
478
|
+
|
|
479
|
+
try {
|
|
480
|
+
new StackClientApp({
|
|
481
|
+
baseUrl: "http://localhost:12345",
|
|
482
|
+
projectId,
|
|
483
|
+
publishableClientKey: "stack-pk-test",
|
|
484
|
+
tokenStore: "memory",
|
|
485
|
+
redirectMethod: "window",
|
|
486
|
+
urls: {
|
|
487
|
+
default: { type: "hosted" },
|
|
488
|
+
},
|
|
489
|
+
noAutomaticPrefetch: true,
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
493
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
494
|
+
} finally {
|
|
495
|
+
redirectSpy.mockRestore();
|
|
496
|
+
globalThis.window = previousWindow;
|
|
497
|
+
globalThis.document = previousDocument;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const errorUrl = new URL(redirectedUrl);
|
|
501
|
+
expect(errorUrl.origin).toBe(`https://${projectId}.built-with-stack-auth.com`);
|
|
502
|
+
expect(errorUrl.pathname).toBe("/handler/error");
|
|
503
|
+
expect(errorUrl.searchParams.get("errorCode")).toBe("SIGN_UP_REJECTED");
|
|
504
|
+
expect(errorUrl.searchParams.get("message")).toBe("Your sign up was rejected by an administrator's sign-up rule.");
|
|
505
|
+
expect(new URL(currentHref).searchParams.has("errorCode")).toBe(false);
|
|
506
|
+
});
|
|
507
|
+
|
|
442
508
|
it("uses direct sign-out instead of hosted sign-out redirects when code execution is available", async () => {
|
|
443
509
|
const clientApp = new StackClientApp({
|
|
444
510
|
baseUrl: "http://localhost:12345",
|