@opengeni/sdk 3.1.0 → 3.1.2
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 +1037 -71
- package/dist/accounts.d.ts +5 -1
- package/dist/accounts.js +13 -1
- package/dist/accounts.js.map +1 -1
- package/dist/adapters/express.d.ts +31 -0
- package/dist/adapters/express.js +87 -0
- package/dist/adapters/express.js.map +1 -0
- package/dist/adapters/hono.d.ts +16 -0
- package/dist/adapters/hono.js +8 -0
- package/dist/adapters/hono.js.map +1 -0
- package/dist/adapters/next.d.ts +29 -0
- package/dist/adapters/next.js +22 -0
- package/dist/adapters/next.js.map +1 -0
- package/dist/allowance-refusal.d.ts +2 -0
- package/dist/allowance-refusal.js +50 -0
- package/dist/allowance-refusal.js.map +1 -0
- package/dist/app-links.d.ts +42 -0
- package/dist/app-links.js +66 -0
- package/dist/app-links.js.map +1 -0
- package/dist/artifact-catalog.d.ts +40 -0
- package/dist/artifact-client.d.ts +20 -8
- package/dist/artifacts.d.ts +1 -1
- package/dist/artifacts.js +8 -6
- package/dist/automations.js +8 -2
- package/dist/automations.js.map +1 -1
- package/dist/azure-live-transport.d.ts +32 -0
- package/dist/browser.d.ts +7 -2
- package/dist/browser.js +27 -5
- package/dist/chat/fold.d.ts +75 -0
- package/dist/chat/handler.d.ts +41 -0
- package/dist/chat/http.d.ts +67 -0
- package/dist/chat/ids.d.ts +34 -0
- package/dist/chat/index.d.ts +9 -0
- package/dist/chat/index.js +1703 -0
- package/dist/chat/index.js.map +1 -0
- package/dist/chat/openai.d.ts +30 -0
- package/dist/chat/opengeni.d.ts +129 -0
- package/dist/chat/types.d.ts +207 -0
- package/dist/chat/vercel.d.ts +49 -0
- package/dist/chats.d.ts +8 -0
- package/dist/chunk-4AHR4KGA.js +301 -0
- package/dist/chunk-4AHR4KGA.js.map +1 -0
- package/dist/chunk-6HZGAIUE.js +1665 -0
- package/dist/chunk-6HZGAIUE.js.map +1 -0
- package/dist/chunk-7EWT3PV6.js +368 -0
- package/dist/chunk-7EWT3PV6.js.map +1 -0
- package/dist/{chunk-VYCTL62C.js → chunk-GZQSWAKD.js} +1 -1
- package/dist/chunk-GZQSWAKD.js.map +1 -0
- package/dist/chunk-JXXCTW5W.js +1392 -0
- package/dist/chunk-JXXCTW5W.js.map +1 -0
- package/dist/{chunk-YTAWBDO2.js → chunk-KADZ5PGG.js} +28 -2
- package/dist/chunk-KADZ5PGG.js.map +1 -0
- package/dist/{chunk-YKZME56C.js → chunk-KWADQ227.js} +3 -3
- package/dist/{chunk-YKZME56C.js.map → chunk-KWADQ227.js.map} +1 -1
- package/dist/{chunk-7ZXBPJZP.js → chunk-NEM5N6C6.js} +3902 -1457
- package/dist/chunk-NEM5N6C6.js.map +1 -0
- package/dist/{chunk-GW3EJ2GP.js → chunk-O7KANXC3.js} +86 -13
- package/dist/chunk-O7KANXC3.js.map +1 -0
- package/dist/{chunk-VUQZAB3O.js → chunk-OAKHWFSV.js} +28 -32
- package/dist/chunk-OAKHWFSV.js.map +1 -0
- package/dist/chunk-P7UWKORG.js +561 -0
- package/dist/chunk-P7UWKORG.js.map +1 -0
- package/dist/chunk-QGEIL4TR.js +195 -0
- package/dist/chunk-QGEIL4TR.js.map +1 -0
- package/dist/chunk-RJHCBZF7.js +270 -0
- package/dist/chunk-RJHCBZF7.js.map +1 -0
- package/dist/chunk-S3D75WSQ.js +340 -0
- package/dist/chunk-S3D75WSQ.js.map +1 -0
- package/dist/chunk-UM4ZXXJR.js +9 -0
- package/dist/chunk-UM4ZXXJR.js.map +1 -0
- package/dist/chunk-VPVMLM7Q.js +15 -0
- package/dist/chunk-VPVMLM7Q.js.map +1 -0
- package/dist/{chunk-C2H7HBNP.js → chunk-WVCJRSGJ.js} +119 -14
- package/dist/chunk-WVCJRSGJ.js.map +1 -0
- package/dist/chunk-XA7HVIHI.js +126 -0
- package/dist/chunk-XA7HVIHI.js.map +1 -0
- package/dist/{chunk-TX3EIENU.js → chunk-YDVPI7WT.js} +73 -2
- package/dist/chunk-YDVPI7WT.js.map +1 -0
- package/dist/chunk-YKIHNLWD.js +153 -0
- package/dist/chunk-YKIHNLWD.js.map +1 -0
- package/dist/chunk-ZDUBPV33.js +277 -0
- package/dist/chunk-ZDUBPV33.js.map +1 -0
- package/dist/client.d.ts +1065 -124
- package/dist/codex-realtime-controller.d.ts +13 -0
- package/dist/codex-realtime-controller.js +4 -2
- package/dist/codex-realtime-v3-wire.d.ts +3 -2
- package/dist/codex-realtime.d.ts +5 -1
- package/dist/company-brain.d.ts +1 -11
- package/dist/company-profile.d.ts +13 -0
- package/dist/core.d.ts +7 -2
- package/dist/core.js +25 -6
- package/dist/deprecation.d.ts +47 -0
- package/dist/document-authority-client.d.ts +24 -2
- package/dist/document-authority.js +7 -5
- package/dist/editable-artifacts/index.d.ts +1 -1
- package/dist/editable-artifacts/types.d.ts +12 -2
- package/dist/editable-artifacts.js +19 -3
- package/dist/editable-artifacts.js.map +1 -1
- package/dist/embedding-client.d.ts +129 -0
- package/dist/errors.d.ts +76 -1
- package/dist/feedback.d.ts +23 -0
- package/dist/gateway-realtime-transport.js +1 -1
- package/dist/github-repositories.d.ts +10 -0
- package/dist/github-repositories.js +36 -0
- package/dist/github-repositories.js.map +1 -0
- package/dist/index.d.ts +64 -11
- package/dist/index.js +566 -141
- package/dist/index.js.map +1 -1
- package/dist/insights-usage.d.ts +33 -0
- package/dist/insights-usage.js +7 -0
- package/dist/insights-usage.js.map +1 -0
- package/dist/interaction.d.ts +103 -71
- package/dist/interaction.js +4 -2
- package/dist/knowledge.d.ts +7 -0
- package/dist/knowledge.js +20 -0
- package/dist/knowledge.js.map +1 -0
- package/dist/known-tools.d.ts +4 -0
- package/dist/message-links.d.ts +68 -0
- package/dist/model-context.d.ts +68 -0
- package/dist/model-display.d.ts +2 -0
- package/dist/model-display.js +17 -0
- package/dist/model-display.js.map +1 -0
- package/dist/model-picker-order.d.ts +1 -0
- package/dist/model-picker-order.js +10 -0
- package/dist/model-picker-order.js.map +1 -0
- package/dist/organization-integration-policy.d.ts +26 -0
- package/dist/organization-integration-policy.js +26 -0
- package/dist/organization-integration-policy.js.map +1 -0
- package/dist/pr-review-client.d.ts +1 -1
- package/dist/pr-review.d.ts +1 -1
- package/dist/pr-review.js.map +1 -1
- package/dist/preference-registry.d.ts +1 -1
- package/dist/prepared-mcp-setup.d.ts +5 -0
- package/dist/proxy.d.ts +13 -11
- package/dist/realtime.d.ts +4 -2
- package/dist/realtime.js +253 -7
- package/dist/realtime.js.map +1 -1
- package/dist/session-history-import.d.ts +22 -0
- package/dist/session-history-import.js +36 -0
- package/dist/session-history-import.js.map +1 -0
- package/dist/session-list-entries.d.ts +1 -0
- package/dist/session-list-entries.js +7 -0
- package/dist/session-list-entries.js.map +1 -0
- package/dist/session-message-search.d.ts +53 -0
- package/dist/session-proxy-client.d.ts +28 -0
- package/dist/session-proxy.d.ts +334 -0
- package/dist/session-proxy.js +24 -0
- package/dist/session-proxy.js.map +1 -0
- package/dist/session-titles.d.ts +2 -0
- package/dist/session-usage-limits.d.ts +10 -0
- package/dist/session-usage-limits.js +43 -0
- package/dist/session-usage-limits.js.map +1 -0
- package/dist/shared-catalog-loader.d.ts +34 -0
- package/dist/site-bridge-host.d.ts +112 -0
- package/dist/site-bridge-host.js +285 -0
- package/dist/site-bridge-host.js.map +1 -0
- package/dist/site-browser-entry.d.ts +1 -0
- package/dist/site-browser-runtime.gen.d.ts +1 -0
- package/dist/site-data.d.ts +178 -0
- package/dist/site-document.d.ts +5 -0
- package/dist/site-document.js +20 -0
- package/dist/site-document.js.map +1 -0
- package/dist/site-document.test.d.ts +1 -0
- package/dist/site-http.d.ts +17 -0
- package/dist/site-sql.d.ts +31 -0
- package/dist/site-tool-bridge.d.ts +57 -0
- package/dist/site.d.ts +182 -0
- package/dist/site.js +62 -0
- package/dist/site.js.map +1 -0
- package/dist/skills.d.ts +75 -0
- package/dist/sse.d.ts +1 -1
- package/dist/stream.d.ts +2 -0
- package/dist/tenant-workspaces.d.ts +47 -0
- package/dist/tenant-workspaces.js +8 -0
- package/dist/tenant-workspaces.js.map +1 -0
- package/dist/tool-auth.d.ts +87 -0
- package/dist/tool-auth.js +23 -0
- package/dist/tool-auth.js.map +1 -0
- package/dist/tool-display-metadata.d.ts +3 -0
- package/dist/tools-37DCTCNZ.js +12 -0
- package/dist/tools-37DCTCNZ.js.map +1 -0
- package/dist/tools.d.ts +81 -0
- package/dist/transcription.d.ts +1 -1
- package/dist/types.d.ts +2848 -573
- package/dist/usage-allowances.d.ts +36 -0
- package/dist/usage-allowances.js +80 -0
- package/dist/usage-allowances.js.map +1 -0
- package/dist/vault-types.d.ts +236 -0
- package/dist/vaults-api.d.ts +57 -0
- package/dist/workspace-artifacts.d.ts +47 -5
- package/dist/workspace-instruction-policies.d.ts +2 -2
- package/dist/workspace-integrations.d.ts +74 -0
- package/dist/workspace-integrations.js +65 -0
- package/dist/workspace-integrations.js.map +1 -0
- package/dist/workspace-state.d.ts +8 -71
- package/package.json +99 -5
- package/src/accounts.ts +18 -0
- package/src/adapters/express.ts +136 -0
- package/src/adapters/hono.ts +18 -0
- package/src/adapters/next.ts +42 -0
- package/src/allowance-refusal.ts +5 -0
- package/src/app-links.ts +117 -0
- package/src/artifact-catalog.ts +51 -0
- package/src/artifact-client.ts +40 -49
- package/src/artifacts.ts +1 -0
- package/src/automations-client.ts +8 -2
- package/src/azure-live-transport.ts +286 -0
- package/src/browser.ts +37 -0
- package/src/chat/fold.ts +431 -0
- package/src/chat/handler.ts +240 -0
- package/src/chat/http.ts +257 -0
- package/src/chat/ids.ts +85 -0
- package/src/chat/index.ts +88 -0
- package/src/chat/openai.ts +439 -0
- package/src/chat/opengeni.ts +737 -0
- package/src/chat/types.ts +219 -0
- package/src/chat/vercel.ts +204 -0
- package/src/chats.ts +16 -0
- package/src/client.ts +5822 -1847
- package/src/codex-realtime-controller.ts +163 -8
- package/src/codex-realtime-v3-wire.ts +4 -3
- package/src/codex-realtime-v3.ts +12 -3
- package/src/codex-realtime.ts +11 -3
- package/src/company-brain.ts +0 -40
- package/src/company-profile.ts +13 -0
- package/src/core.ts +20 -0
- package/src/deprecation.ts +149 -0
- package/src/document-authority-client.ts +104 -1
- package/src/editable-artifacts/controller.ts +36 -2
- package/src/editable-artifacts/index.ts +1 -0
- package/src/editable-artifacts/types.ts +29 -16
- package/src/embedding-client.ts +519 -0
- package/src/errors.ts +303 -4
- package/src/feedback.ts +20 -0
- package/src/gateway-realtime-transport.ts +3 -3
- package/src/github-repositories.ts +60 -0
- package/src/index.ts +611 -64
- package/src/insights-usage.ts +61 -0
- package/src/interaction.ts +274 -93
- package/src/knowledge.ts +72 -0
- package/src/known-tools.ts +279 -0
- package/src/message-links.ts +178 -0
- package/src/model-context.ts +113 -0
- package/src/model-display.ts +11 -0
- package/src/model-picker-order.ts +6 -0
- package/src/organization-integration-policy.ts +54 -0
- package/src/pr-review-client.ts +1 -1
- package/src/pr-review.ts +1 -1
- package/src/preference-registry.ts +2 -0
- package/src/prepared-mcp-setup.ts +123 -0
- package/src/proxy.ts +23 -12
- package/src/realtime.ts +16 -6
- package/src/session-history-import.ts +84 -0
- package/src/session-list-entries.ts +1 -0
- package/src/session-message-search.ts +47 -0
- package/src/session-proxy-client.ts +68 -0
- package/src/session-proxy.ts +2098 -0
- package/src/session-titles.ts +10 -0
- package/src/session-usage-limits.ts +71 -0
- package/src/shared-catalog-loader.ts +169 -0
- package/src/site-bridge-host.ts +422 -0
- package/src/site-browser-entry.ts +6 -0
- package/src/site-browser-runtime.gen.ts +2 -0
- package/src/site-data.ts +372 -0
- package/src/site-document.test.ts +64 -0
- package/src/site-document.ts +18 -0
- package/src/site-http.ts +171 -0
- package/src/site-sql.ts +153 -0
- package/src/site-tool-bridge.ts +401 -0
- package/src/site.ts +867 -0
- package/src/skills.ts +69 -0
- package/src/sse.ts +9 -3
- package/src/stream.ts +33 -10
- package/src/tenant-workspaces.ts +148 -0
- package/src/tool-auth.ts +301 -0
- package/src/tool-display-metadata.ts +27 -0
- package/src/tools.ts +452 -0
- package/src/transcription.ts +3 -0
- package/src/types.ts +3216 -581
- package/src/usage-allowances.ts +189 -0
- package/src/vault-types.ts +293 -0
- package/src/vaults-api.ts +288 -0
- package/src/workspace-artifacts.ts +55 -6
- package/src/workspace-instruction-policies.ts +2 -1
- package/src/workspace-integrations.ts +402 -0
- package/src/workspace-state.ts +8 -82
- package/dist/chunk-7ZXBPJZP.js.map +0 -1
- package/dist/chunk-C2H7HBNP.js.map +0 -1
- package/dist/chunk-C4DJPZRU.js +0 -119
- package/dist/chunk-C4DJPZRU.js.map +0 -1
- package/dist/chunk-GW3EJ2GP.js.map +0 -1
- package/dist/chunk-TX3EIENU.js.map +0 -1
- package/dist/chunk-VUQZAB3O.js.map +0 -1
- package/dist/chunk-VYCTL62C.js.map +0 -1
- package/dist/chunk-YTAWBDO2.js.map +0 -1
package/dist/accounts.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type BeginManagedAuthLoginTransactionRequest as BeginManagedAuthLoginTransactionRequestType, type BootstrapManagedAuthSessionSetRequest as BootstrapManagedAuthSessionSetRequestType, type CancelManagedAuthLoginTransactionRequest as CancelManagedAuthLoginTransactionRequestType, type CompleteManagedAuthEmailPasswordTransactionRequest as CompleteManagedAuthEmailPasswordTransactionRequestType, type CompleteManagedAuthLoginTransactionResponse as CompleteManagedAuthLoginTransactionResponseType, type LogoutManagedAuthLoginSlotRequest as LogoutManagedAuthLoginSlotRequestType, type LogoutManagedAuthSessionSetRequest as LogoutManagedAuthSessionSetRequestType, type ManagedAuthDeepLinkResolution as ManagedAuthDeepLinkResolutionType, type ManagedAuthLoginTransaction as ManagedAuthLoginTransactionType, type ManagedAuthLogoutAllReceipt as ManagedAuthLogoutAllReceiptType, type ManagedAuthSessionSetProjection as ManagedAuthSessionSetProjectionType, type ResolveManagedAuthDeepLinkRequest as ResolveManagedAuthDeepLinkRequestType, type SelectManagedAuthLoginSlotRequest as SelectManagedAuthLoginSlotRequestType } from "@opengeni/contracts/managed-auth-session-sets";
|
|
1
|
+
import { type BeginManagedAuthLoginTransactionRequest as BeginManagedAuthLoginTransactionRequestType, type BootstrapManagedAuthSessionSetRequest as BootstrapManagedAuthSessionSetRequestType, type CancelManagedAuthLoginTransactionRequest as CancelManagedAuthLoginTransactionRequestType, type CompleteManagedAuthEmailPasswordTransactionRequest as CompleteManagedAuthEmailPasswordTransactionRequestType, type CompleteManagedAuthLoginTransactionResponse as CompleteManagedAuthLoginTransactionResponseType, type LogoutManagedAuthLoginSlotRequest as LogoutManagedAuthLoginSlotRequestType, type LogoutManagedAuthSessionSetRequest as LogoutManagedAuthSessionSetRequestType, type ManagedAuthDeepLinkResolution as ManagedAuthDeepLinkResolutionType, type ManagedAuthLoginTransaction as ManagedAuthLoginTransactionType, type ManagedAuthLogoutAllReceipt as ManagedAuthLogoutAllReceiptType, type ManagedAuthSessionSetProjection as ManagedAuthSessionSetProjectionType, type ResolveManagedAuthDeepLinkRequest as ResolveManagedAuthDeepLinkRequestType, type SelectManagedAuthLoginSlotRequest as SelectManagedAuthLoginSlotRequestType, type StartManagedAuthSocialTransactionRequest as StartManagedAuthSocialTransactionRequestType, type StartManagedAuthSocialTransactionResponse as StartManagedAuthSocialTransactionResponseType } from "@opengeni/contracts/managed-auth-session-sets";
|
|
2
2
|
export type BrowserAccountsFetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
3
3
|
export type BrowserAccountsClientOptions = {
|
|
4
4
|
baseUrl: string;
|
|
@@ -10,6 +10,7 @@ export interface BrowserAccountsClientLike {
|
|
|
10
10
|
bootstrapSessionSet(request: BootstrapManagedAuthSessionSetRequestType): Promise<ManagedAuthSessionSetProjectionType>;
|
|
11
11
|
beginLoginTransaction(request: BeginManagedAuthLoginTransactionRequestType): Promise<ManagedAuthLoginTransactionType>;
|
|
12
12
|
completeEmailPasswordTransaction(request: CompleteManagedAuthEmailPasswordTransactionRequestType): Promise<CompleteManagedAuthLoginTransactionResponseType>;
|
|
13
|
+
startSocialTransaction?(request: StartManagedAuthSocialTransactionRequestType): Promise<StartManagedAuthSocialTransactionResponseType>;
|
|
13
14
|
cancelLoginTransaction(request: CancelManagedAuthLoginTransactionRequestType): Promise<ManagedAuthSessionSetProjectionType>;
|
|
14
15
|
selectLoginSlot(request: SelectManagedAuthLoginSlotRequestType): Promise<ManagedAuthSessionSetProjectionType>;
|
|
15
16
|
logoutLoginSlot(request: LogoutManagedAuthLoginSlotRequestType): Promise<ManagedAuthSessionSetProjectionType>;
|
|
@@ -72,6 +73,9 @@ export declare class BrowserAccountsClient implements BrowserAccountsClientLike
|
|
|
72
73
|
};
|
|
73
74
|
returnIntent: string | null;
|
|
74
75
|
}>;
|
|
76
|
+
startSocialTransaction(request: StartManagedAuthSocialTransactionRequestType): Promise<{
|
|
77
|
+
url: string;
|
|
78
|
+
}>;
|
|
75
79
|
cancelLoginTransaction(request: CancelManagedAuthLoginTransactionRequestType): Promise<{
|
|
76
80
|
mode: "broker" | "dual" | "legacy";
|
|
77
81
|
generation: string;
|
package/dist/accounts.js
CHANGED
|
@@ -15,7 +15,9 @@ import {
|
|
|
15
15
|
ManagedAuthSessionSetProjection,
|
|
16
16
|
ManagedAuthSessionSetErrorCode,
|
|
17
17
|
ResolveManagedAuthDeepLinkRequest,
|
|
18
|
-
SelectManagedAuthLoginSlotRequest
|
|
18
|
+
SelectManagedAuthLoginSlotRequest,
|
|
19
|
+
StartManagedAuthSocialTransactionRequest,
|
|
20
|
+
StartManagedAuthSocialTransactionResponse
|
|
19
21
|
} from "@opengeni/contracts/managed-auth-session-sets";
|
|
20
22
|
var BrowserAccountsApiError = class extends Error {
|
|
21
23
|
constructor(status, code, options) {
|
|
@@ -105,6 +107,16 @@ var BrowserAccountsClient = class {
|
|
|
105
107
|
}
|
|
106
108
|
return response;
|
|
107
109
|
}
|
|
110
|
+
async startSocialTransaction(request) {
|
|
111
|
+
return StartManagedAuthSocialTransactionResponse.parse(
|
|
112
|
+
await this.#mutation(
|
|
113
|
+
"POST",
|
|
114
|
+
"/v1/auth/session-set/transactions/social",
|
|
115
|
+
StartManagedAuthSocialTransactionRequest,
|
|
116
|
+
request
|
|
117
|
+
)
|
|
118
|
+
);
|
|
119
|
+
}
|
|
108
120
|
async cancelLoginTransaction(request) {
|
|
109
121
|
return this.#projectionResponse(
|
|
110
122
|
"DELETE",
|
package/dist/accounts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/accounts.ts"],"sourcesContent":["import {\n BeginManagedAuthLoginTransactionRequest,\n BootstrapManagedAuthSessionSetRequest,\n CancelManagedAuthLoginTransactionRequest,\n CompleteManagedAuthEmailPasswordTransactionRequest,\n CompleteManagedAuthLoginTransactionResponse,\n LogoutManagedAuthLoginSlotRequest,\n LogoutManagedAuthSessionSetRequest,\n MANAGED_AUTH_SESSION_SET_API_CONTRACT_HEADER,\n MANAGED_AUTH_SESSION_SET_API_CONTRACT_REVISION,\n ManagedAuthDeepLinkResolution,\n ManagedAuthLoginTransaction,\n ManagedAuthLogoutAllReceipt,\n ManagedAuthSessionSetProjection,\n ManagedAuthSessionSetErrorCode,\n ResolveManagedAuthDeepLinkRequest,\n SelectManagedAuthLoginSlotRequest,\n type BeginManagedAuthLoginTransactionRequest as BeginManagedAuthLoginTransactionRequestType,\n type BootstrapManagedAuthSessionSetRequest as BootstrapManagedAuthSessionSetRequestType,\n type CancelManagedAuthLoginTransactionRequest as CancelManagedAuthLoginTransactionRequestType,\n type CompleteManagedAuthEmailPasswordTransactionRequest as CompleteManagedAuthEmailPasswordTransactionRequestType,\n type CompleteManagedAuthLoginTransactionResponse as CompleteManagedAuthLoginTransactionResponseType,\n type LogoutManagedAuthLoginSlotRequest as LogoutManagedAuthLoginSlotRequestType,\n type LogoutManagedAuthSessionSetRequest as LogoutManagedAuthSessionSetRequestType,\n type ManagedAuthDeepLinkResolution as ManagedAuthDeepLinkResolutionType,\n type ManagedAuthLoginTransaction as ManagedAuthLoginTransactionType,\n type ManagedAuthLogoutAllReceipt as ManagedAuthLogoutAllReceiptType,\n type ManagedAuthSessionSetProjection as ManagedAuthSessionSetProjectionType,\n type ResolveManagedAuthDeepLinkRequest as ResolveManagedAuthDeepLinkRequestType,\n type SelectManagedAuthLoginSlotRequest as SelectManagedAuthLoginSlotRequestType,\n} from \"@opengeni/contracts/managed-auth-session-sets\";\nimport type { z } from \"zod\";\n\nexport type BrowserAccountsFetch = (\n input: string | URL | Request,\n init?: RequestInit,\n) => Promise<Response>;\n\nexport type BrowserAccountsClientOptions = {\n baseUrl: string;\n fetch?: BrowserAccountsFetch;\n};\n\nexport interface BrowserAccountsClientLike {\n getSessionSet(): Promise<ManagedAuthSessionSetProjectionType>;\n reconcileSessionSetAuthority(): Promise<ManagedAuthSessionSetProjectionType>;\n bootstrapSessionSet(\n request: BootstrapManagedAuthSessionSetRequestType,\n ): Promise<ManagedAuthSessionSetProjectionType>;\n beginLoginTransaction(\n request: BeginManagedAuthLoginTransactionRequestType,\n ): Promise<ManagedAuthLoginTransactionType>;\n completeEmailPasswordTransaction(\n request: CompleteManagedAuthEmailPasswordTransactionRequestType,\n ): Promise<CompleteManagedAuthLoginTransactionResponseType>;\n cancelLoginTransaction(\n request: CancelManagedAuthLoginTransactionRequestType,\n ): Promise<ManagedAuthSessionSetProjectionType>;\n selectLoginSlot(\n request: SelectManagedAuthLoginSlotRequestType,\n ): Promise<ManagedAuthSessionSetProjectionType>;\n logoutLoginSlot(\n request: LogoutManagedAuthLoginSlotRequestType,\n ): Promise<ManagedAuthSessionSetProjectionType>;\n logoutSessionSet(\n request: LogoutManagedAuthSessionSetRequestType,\n ): Promise<ManagedAuthLogoutAllReceiptType>;\n resolveDeepLink(\n request: ResolveManagedAuthDeepLinkRequestType,\n ): Promise<ManagedAuthDeepLinkResolutionType>;\n}\n\nexport class BrowserAccountsApiError extends Error {\n readonly name = \"BrowserAccountsApiError\";\n constructor(\n readonly status: number,\n readonly code: string,\n options?: { cause?: unknown },\n ) {\n super(code, options?.cause === undefined ? undefined : { cause: options.cause });\n }\n}\n\nexport class BrowserAccountsClient implements BrowserAccountsClientLike {\n readonly #baseUrl: string;\n readonly #fetch: BrowserAccountsFetch;\n // CSRF is generation-bound. Exact idempotent replay must retain the\n // admission envelope even after an authority reread observes a newer clock.\n readonly #mutationAdmissions = new Map<\n string,\n { signature: string; csrfToken: string; actorEpoch: string }\n >();\n #projection: ManagedAuthSessionSetProjectionType | null = null;\n #authorityReadEpoch = 0;\n #authorityResetPending = false;\n #authorityReconciliation: Promise<ManagedAuthSessionSetProjectionType> | null = null;\n\n constructor(options: BrowserAccountsClientOptions) {\n this.#baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.#fetch = options.fetch ?? ((input, init) => fetch(input, init));\n }\n\n async getSessionSet(): Promise<ManagedAuthSessionSetProjectionType> {\n if (this.#authorityResetPending || this.#authorityReconciliation) {\n return await this.reconcileSessionSetAuthority();\n }\n const readEpoch = this.#authorityReadEpoch;\n const received = await this.#readSessionSet();\n if (readEpoch !== this.#authorityReadEpoch) {\n return this.#authorityReconciliation\n ? await this.#authorityReconciliation\n : (this.#projection ?? (await this.getSessionSet()));\n }\n return this.#remember(received);\n }\n\n async reconcileSessionSetAuthority(): Promise<ManagedAuthSessionSetProjectionType> {\n if (this.#authorityReconciliation) return await this.#authorityReconciliation;\n const previous = this.#projection;\n const readEpoch = ++this.#authorityReadEpoch;\n // Explicit reconciliation is an authority-invalidation boundary, not an\n // ordinary monotonic refresh. Old operation admissions must never cross it,\n // and mutations stay closed until two sequential reads reach a decision.\n this.#authorityResetPending = true;\n this.#mutationAdmissions.clear();\n const reconciliation = this.#reconcileSessionSetAuthority(previous, readEpoch);\n this.#authorityReconciliation = reconciliation;\n try {\n const accepted = await reconciliation;\n if (readEpoch === this.#authorityReadEpoch) this.#authorityResetPending = false;\n return accepted;\n } finally {\n if (this.#authorityReconciliation === reconciliation) {\n this.#authorityReconciliation = null;\n }\n }\n }\n\n async bootstrapSessionSet(request: BootstrapManagedAuthSessionSetRequestType) {\n return this.#projectionResponse(\n \"POST\",\n \"/v1/auth/session-set/bootstrap\",\n BootstrapManagedAuthSessionSetRequest,\n request,\n );\n }\n\n async beginLoginTransaction(request: BeginManagedAuthLoginTransactionRequestType) {\n return ManagedAuthLoginTransaction.parse(\n await this.#mutation(\n \"POST\",\n \"/v1/auth/session-set/transactions\",\n BeginManagedAuthLoginTransactionRequest,\n request,\n ),\n );\n }\n\n async completeEmailPasswordTransaction(\n request: CompleteManagedAuthEmailPasswordTransactionRequestType,\n ) {\n const authorityReadEpoch = this.#authorityReadEpoch;\n const response = CompleteManagedAuthLoginTransactionResponse.parse(\n await this.#mutation(\n \"POST\",\n \"/v1/auth/session-set/transactions/email-password\",\n CompleteManagedAuthEmailPasswordTransactionRequest,\n request,\n ),\n );\n if (authorityReadEpoch !== this.#authorityReadEpoch) {\n return { ...response, projection: await this.getSessionSet() };\n }\n const remembered = this.#remember(response.projection);\n if (remembered !== response.projection) {\n return { ...response, projection: await this.getSessionSet() };\n }\n return response;\n }\n\n async cancelLoginTransaction(request: CancelManagedAuthLoginTransactionRequestType) {\n return this.#projectionResponse(\n \"DELETE\",\n `/v1/auth/session-set/transactions/${encodeURIComponent(request.transactionId)}`,\n CancelManagedAuthLoginTransactionRequest,\n request,\n );\n }\n\n async selectLoginSlot(request: SelectManagedAuthLoginSlotRequestType) {\n return this.#projectionResponse(\n \"POST\",\n \"/v1/auth/session-set/select\",\n SelectManagedAuthLoginSlotRequest,\n request,\n );\n }\n\n async logoutLoginSlot(request: LogoutManagedAuthLoginSlotRequestType) {\n return this.#projectionResponse(\n \"POST\",\n \"/v1/auth/session-set/logout-one\",\n LogoutManagedAuthLoginSlotRequest,\n request,\n );\n }\n\n async logoutSessionSet(request: LogoutManagedAuthSessionSetRequestType) {\n const receipt = ManagedAuthLogoutAllReceipt.parse(\n await this.#mutation(\n \"POST\",\n \"/v1/auth/session-set/logout-all\",\n LogoutManagedAuthSessionSetRequest,\n request,\n ),\n );\n this.#authorityReadEpoch += 1;\n this.#authorityResetPending = false;\n this.#projection = null;\n this.#mutationAdmissions.clear();\n return receipt;\n }\n\n async resolveDeepLink(request: ResolveManagedAuthDeepLinkRequestType) {\n return ManagedAuthDeepLinkResolution.parse(\n await this.#mutation(\n \"POST\",\n \"/v1/auth/session-set/deep-link/resolve\",\n ResolveManagedAuthDeepLinkRequest,\n request,\n ),\n );\n }\n\n async #projectionResponse<S extends z.ZodType>(\n method: \"POST\" | \"DELETE\",\n path: string,\n schema: S,\n request: z.infer<S>,\n ): Promise<ManagedAuthSessionSetProjectionType> {\n const authorityReadEpoch = this.#authorityReadEpoch;\n const received = ManagedAuthSessionSetProjection.parse(\n await this.#mutation(method, path, schema, request),\n );\n if (authorityReadEpoch !== this.#authorityReadEpoch) return await this.getSessionSet();\n const remembered = this.#remember(received);\n // An exact replay may legitimately return its older durable receipt after\n // another tab has advanced this in-memory client. Reconcile from authority\n // instead of presenting that receipt as the current actor projection.\n return remembered === received ? received : await this.getSessionSet();\n }\n\n async #mutation<S extends z.ZodType>(\n method: \"POST\" | \"DELETE\",\n path: string,\n schema: S,\n request: z.infer<S>,\n ): Promise<unknown> {\n if (this.#authorityResetPending) {\n throw new BrowserAccountsApiError(409, \"actor_change_required\");\n }\n const body = schema.parse(request);\n const projection = this.#projection;\n if (!projection) throw new BrowserAccountsApiError(409, \"session_set_projection_required\");\n const operationId = mutationOperationId(body);\n const signature = operationId ? JSON.stringify([method, path, body]) : null;\n const retained = operationId ? this.#mutationAdmissions.get(operationId) : null;\n if (retained && retained.signature !== signature) {\n throw new BrowserAccountsApiError(409, \"operation_reused\");\n }\n const admission =\n retained ??\n ({\n signature: signature ?? \"\",\n csrfToken: projection.csrfToken,\n actorEpoch: projection.actorEpoch,\n } as const);\n if (operationId && !retained) {\n this.#mutationAdmissions.set(operationId, admission);\n if (this.#mutationAdmissions.size > 64) {\n this.#mutationAdmissions.delete(this.#mutationAdmissions.keys().next().value!);\n }\n }\n return await this.#request(method, path, body, {\n \"x-opengeni-session-csrf\": admission.csrfToken,\n \"x-opengeni-actor-epoch\": admission.actorEpoch,\n });\n }\n\n async #request(\n method: \"GET\" | \"POST\" | \"DELETE\",\n path: string,\n body?: unknown,\n headers: Record<string, string> = {},\n ): Promise<unknown> {\n let response: Response;\n try {\n response = await this.#fetch(`${this.#baseUrl}${path}`, {\n method,\n credentials: \"include\",\n headers: {\n accept: \"application/json\",\n [MANAGED_AUTH_SESSION_SET_API_CONTRACT_HEADER]:\n MANAGED_AUTH_SESSION_SET_API_CONTRACT_REVISION,\n ...(body === undefined ? {} : { \"content-type\": \"application/json\" }),\n ...headers,\n },\n ...(body === undefined ? {} : { body: JSON.stringify(body) }),\n });\n } catch (cause) {\n if (method !== \"GET\") {\n throw new BrowserAccountsApiError(503, \"operation_outcome_unknown\", { cause });\n }\n throw cause;\n }\n let parsed = true;\n const value = await response.json().catch(() => {\n parsed = false;\n return null;\n });\n if (!response.ok) {\n const code = managedAuthErrorCode(value) ?? `http_${response.status}`;\n throw new BrowserAccountsApiError(response.status, code);\n }\n if (!parsed && method !== \"GET\") {\n throw new BrowserAccountsApiError(503, \"operation_outcome_unknown\");\n }\n return value;\n }\n\n async #readSessionSet(): Promise<ManagedAuthSessionSetProjectionType> {\n return ManagedAuthSessionSetProjection.parse(\n await this.#request(\"GET\", \"/v1/auth/session-set\"),\n );\n }\n\n async #reconcileSessionSetAuthority(\n previous: ManagedAuthSessionSetProjectionType | null,\n readEpoch: number,\n ): Promise<ManagedAuthSessionSetProjectionType> {\n const first = await this.#readSessionSet();\n const second = await this.#readSessionSet();\n if (readEpoch !== this.#authorityReadEpoch) {\n return await this.#reconcileSessionSetAuthority(this.#projection, this.#authorityReadEpoch);\n }\n if (this.#projection !== previous) return this.#remember(second);\n if (!previous || compareProjectionClock(second, previous) >= 0) {\n return this.#remember(second);\n }\n if (compareProjectionClock(first, previous) < 0 && compareProjectionClock(second, first) >= 0) {\n // Same-authority clocks never decrease. Two sequential no-store reads\n // that both remain below the remembered clock therefore prove that the\n // HttpOnly authority rotated; the second may already have advanced from\n // the fresh authority's initial 1/1 projection.\n this.#projection = second;\n this.#mutationAdmissions.clear();\n return second;\n }\n return previous;\n }\n\n #remember(projection: ManagedAuthSessionSetProjectionType) {\n const current = this.#projection;\n if (!current || compareProjectionClock(projection, current) >= 0) {\n this.#projection = projection;\n return projection;\n }\n return current;\n }\n}\n\nfunction mutationOperationId(value: unknown): string | null {\n if (!value || typeof value !== \"object\") return null;\n const operationId = (value as { operationId?: unknown }).operationId;\n return typeof operationId === \"string\" ? operationId : null;\n}\n\nfunction compareProjectionClock(\n left: Pick<ManagedAuthSessionSetProjectionType, \"actorEpoch\" | \"generation\">,\n right: Pick<ManagedAuthSessionSetProjectionType, \"actorEpoch\" | \"generation\">,\n): number {\n const actorDelta = BigInt(left.actorEpoch) - BigInt(right.actorEpoch);\n if (actorDelta !== 0n) return actorDelta > 0n ? 1 : -1;\n const generationDelta = BigInt(left.generation) - BigInt(right.generation);\n return generationDelta === 0n ? 0 : generationDelta > 0n ? 1 : -1;\n}\n\nfunction managedAuthErrorCode(value: unknown): string | null {\n if (!value || typeof value !== \"object\") return null;\n const error = (value as { error?: unknown }).error;\n if (error && typeof error === \"object\") {\n const details = (error as { details?: unknown }).details;\n if (details && typeof details === \"object\") {\n const parsed = ManagedAuthSessionSetErrorCode.safeParse(\n (details as { managedAuthCode?: unknown }).managedAuthCode,\n );\n if (parsed.success) return parsed.data;\n }\n }\n const legacy = (value as { message?: unknown }).message;\n const parsed = ManagedAuthSessionSetErrorCode.safeParse(legacy);\n return parsed.success ? parsed.data : null;\n}\n\nexport function createBrowserAccountsClient(\n options: BrowserAccountsClientOptions,\n): BrowserAccountsClient {\n return new BrowserAccountsClient(options);\n}\n\nexport type {\n BeginManagedAuthLoginTransactionRequestType as BeginManagedAuthLoginTransactionRequest,\n BootstrapManagedAuthSessionSetRequestType as BootstrapManagedAuthSessionSetRequest,\n CancelManagedAuthLoginTransactionRequestType as CancelManagedAuthLoginTransactionRequest,\n CompleteManagedAuthEmailPasswordTransactionRequestType as CompleteManagedAuthEmailPasswordTransactionRequest,\n CompleteManagedAuthLoginTransactionResponseType as CompleteManagedAuthLoginTransactionResponse,\n LogoutManagedAuthLoginSlotRequestType as LogoutManagedAuthLoginSlotRequest,\n LogoutManagedAuthSessionSetRequestType as LogoutManagedAuthSessionSetRequest,\n ManagedAuthDeepLinkResolutionType as ManagedAuthDeepLinkResolution,\n ManagedAuthLoginTransactionType as ManagedAuthLoginTransaction,\n ManagedAuthLogoutAllReceiptType as ManagedAuthLogoutAllReceipt,\n ManagedAuthSessionSetProjectionType as ManagedAuthSessionSetProjection,\n ResolveManagedAuthDeepLinkRequestType as ResolveManagedAuthDeepLinkRequest,\n SelectManagedAuthLoginSlotRequestType as SelectManagedAuthLoginSlotRequest,\n};\n"],"mappings":";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAcK;AA0CA,IAAM,0BAAN,cAAsC,MAAM;AAAA,EAEjD,YACW,QACA,MACT,SACA;AACA,UAAM,MAAM,SAAS,UAAU,SAAY,SAAY,EAAE,OAAO,QAAQ,MAAM,CAAC;AAJtE;AACA;AAAA,EAIX;AAAA,EAPS,OAAO;AAQlB;AAEO,IAAM,wBAAN,MAAiE;AAAA,EAC7D;AAAA,EACA;AAAA;AAAA;AAAA,EAGA,sBAAsB,oBAAI,IAGjC;AAAA,EACF,cAA0D;AAAA,EAC1D,sBAAsB;AAAA,EACtB,yBAAyB;AAAA,EACzB,2BAAgF;AAAA,EAEhF,YAAY,SAAuC;AACjD,SAAK,WAAW,QAAQ,QAAQ,QAAQ,QAAQ,EAAE;AAClD,SAAK,SAAS,QAAQ,UAAU,CAAC,OAAO,SAAS,MAAM,OAAO,IAAI;AAAA,EACpE;AAAA,EAEA,MAAM,gBAA8D;AAClE,QAAI,KAAK,0BAA0B,KAAK,0BAA0B;AAChE,aAAO,MAAM,KAAK,6BAA6B;AAAA,IACjD;AACA,UAAM,YAAY,KAAK;AACvB,UAAM,WAAW,MAAM,KAAK,gBAAgB;AAC5C,QAAI,cAAc,KAAK,qBAAqB;AAC1C,aAAO,KAAK,2BACR,MAAM,KAAK,2BACV,KAAK,eAAgB,MAAM,KAAK,cAAc;AAAA,IACrD;AACA,WAAO,KAAK,UAAU,QAAQ;AAAA,EAChC;AAAA,EAEA,MAAM,+BAA6E;AACjF,QAAI,KAAK,yBAA0B,QAAO,MAAM,KAAK;AACrD,UAAM,WAAW,KAAK;AACtB,UAAM,YAAY,EAAE,KAAK;AAIzB,SAAK,yBAAyB;AAC9B,SAAK,oBAAoB,MAAM;AAC/B,UAAM,iBAAiB,KAAK,8BAA8B,UAAU,SAAS;AAC7E,SAAK,2BAA2B;AAChC,QAAI;AACF,YAAM,WAAW,MAAM;AACvB,UAAI,cAAc,KAAK,oBAAqB,MAAK,yBAAyB;AAC1E,aAAO;AAAA,IACT,UAAE;AACA,UAAI,KAAK,6BAA6B,gBAAgB;AACpD,aAAK,2BAA2B;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,oBAAoB,SAAoD;AAC5E,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,sBAAsB,SAAsD;AAChF,WAAO,4BAA4B;AAAA,MACjC,MAAM,KAAK;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,iCACJ,SACA;AACA,UAAM,qBAAqB,KAAK;AAChC,UAAM,WAAW,4CAA4C;AAAA,MAC3D,MAAM,KAAK;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,uBAAuB,KAAK,qBAAqB;AACnD,aAAO,EAAE,GAAG,UAAU,YAAY,MAAM,KAAK,cAAc,EAAE;AAAA,IAC/D;AACA,UAAM,aAAa,KAAK,UAAU,SAAS,UAAU;AACrD,QAAI,eAAe,SAAS,YAAY;AACtC,aAAO,EAAE,GAAG,UAAU,YAAY,MAAM,KAAK,cAAc,EAAE;AAAA,IAC/D;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,uBAAuB,SAAuD;AAClF,WAAO,KAAK;AAAA,MACV;AAAA,MACA,qCAAqC,mBAAmB,QAAQ,aAAa,CAAC;AAAA,MAC9E;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,gBAAgB,SAAgD;AACpE,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,gBAAgB,SAAgD;AACpE,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,iBAAiB,SAAiD;AACtE,UAAM,UAAU,4BAA4B;AAAA,MAC1C,MAAM,KAAK;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,SAAK,uBAAuB;AAC5B,SAAK,yBAAyB;AAC9B,SAAK,cAAc;AACnB,SAAK,oBAAoB,MAAM;AAC/B,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,gBAAgB,SAAgD;AACpE,WAAO,8BAA8B;AAAA,MACnC,MAAM,KAAK;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,oBACJ,QACA,MACA,QACA,SAC8C;AAC9C,UAAM,qBAAqB,KAAK;AAChC,UAAM,WAAW,gCAAgC;AAAA,MAC/C,MAAM,KAAK,UAAU,QAAQ,MAAM,QAAQ,OAAO;AAAA,IACpD;AACA,QAAI,uBAAuB,KAAK,oBAAqB,QAAO,MAAM,KAAK,cAAc;AACrF,UAAM,aAAa,KAAK,UAAU,QAAQ;AAI1C,WAAO,eAAe,WAAW,WAAW,MAAM,KAAK,cAAc;AAAA,EACvE;AAAA,EAEA,MAAM,UACJ,QACA,MACA,QACA,SACkB;AAClB,QAAI,KAAK,wBAAwB;AAC/B,YAAM,IAAI,wBAAwB,KAAK,uBAAuB;AAAA,IAChE;AACA,UAAM,OAAO,OAAO,MAAM,OAAO;AACjC,UAAM,aAAa,KAAK;AACxB,QAAI,CAAC,WAAY,OAAM,IAAI,wBAAwB,KAAK,iCAAiC;AACzF,UAAM,cAAc,oBAAoB,IAAI;AAC5C,UAAM,YAAY,cAAc,KAAK,UAAU,CAAC,QAAQ,MAAM,IAAI,CAAC,IAAI;AACvE,UAAM,WAAW,cAAc,KAAK,oBAAoB,IAAI,WAAW,IAAI;AAC3E,QAAI,YAAY,SAAS,cAAc,WAAW;AAChD,YAAM,IAAI,wBAAwB,KAAK,kBAAkB;AAAA,IAC3D;AACA,UAAM,YACJ,YACC;AAAA,MACC,WAAW,aAAa;AAAA,MACxB,WAAW,WAAW;AAAA,MACtB,YAAY,WAAW;AAAA,IACzB;AACF,QAAI,eAAe,CAAC,UAAU;AAC5B,WAAK,oBAAoB,IAAI,aAAa,SAAS;AACnD,UAAI,KAAK,oBAAoB,OAAO,IAAI;AACtC,aAAK,oBAAoB,OAAO,KAAK,oBAAoB,KAAK,EAAE,KAAK,EAAE,KAAM;AAAA,MAC/E;AAAA,IACF;AACA,WAAO,MAAM,KAAK,SAAS,QAAQ,MAAM,MAAM;AAAA,MAC7C,2BAA2B,UAAU;AAAA,MACrC,0BAA0B,UAAU;AAAA,IACtC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,SACJ,QACA,MACA,MACA,UAAkC,CAAC,GACjB;AAClB,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,KAAK,OAAO,GAAG,KAAK,QAAQ,GAAG,IAAI,IAAI;AAAA,QACtD;AAAA,QACA,aAAa;AAAA,QACb,SAAS;AAAA,UACP,QAAQ;AAAA,UACR,CAAC,4CAA4C,GAC3C;AAAA,UACF,GAAI,SAAS,SAAY,CAAC,IAAI,EAAE,gBAAgB,mBAAmB;AAAA,UACnE,GAAG;AAAA,QACL;AAAA,QACA,GAAI,SAAS,SAAY,CAAC,IAAI,EAAE,MAAM,KAAK,UAAU,IAAI,EAAE;AAAA,MAC7D,CAAC;AAAA,IACH,SAAS,OAAO;AACd,UAAI,WAAW,OAAO;AACpB,cAAM,IAAI,wBAAwB,KAAK,6BAA6B,EAAE,MAAM,CAAC;AAAA,MAC/E;AACA,YAAM;AAAA,IACR;AACA,QAAI,SAAS;AACb,UAAM,QAAQ,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM;AAC9C,eAAS;AACT,aAAO;AAAA,IACT,CAAC;AACD,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,qBAAqB,KAAK,KAAK,QAAQ,SAAS,MAAM;AACnE,YAAM,IAAI,wBAAwB,SAAS,QAAQ,IAAI;AAAA,IACzD;AACA,QAAI,CAAC,UAAU,WAAW,OAAO;AAC/B,YAAM,IAAI,wBAAwB,KAAK,2BAA2B;AAAA,IACpE;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,kBAAgE;AACpE,WAAO,gCAAgC;AAAA,MACrC,MAAM,KAAK,SAAS,OAAO,sBAAsB;AAAA,IACnD;AAAA,EACF;AAAA,EAEA,MAAM,8BACJ,UACA,WAC8C;AAC9C,UAAM,QAAQ,MAAM,KAAK,gBAAgB;AACzC,UAAM,SAAS,MAAM,KAAK,gBAAgB;AAC1C,QAAI,cAAc,KAAK,qBAAqB;AAC1C,aAAO,MAAM,KAAK,8BAA8B,KAAK,aAAa,KAAK,mBAAmB;AAAA,IAC5F;AACA,QAAI,KAAK,gBAAgB,SAAU,QAAO,KAAK,UAAU,MAAM;AAC/D,QAAI,CAAC,YAAY,uBAAuB,QAAQ,QAAQ,KAAK,GAAG;AAC9D,aAAO,KAAK,UAAU,MAAM;AAAA,IAC9B;AACA,QAAI,uBAAuB,OAAO,QAAQ,IAAI,KAAK,uBAAuB,QAAQ,KAAK,KAAK,GAAG;AAK7F,WAAK,cAAc;AACnB,WAAK,oBAAoB,MAAM;AAC/B,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EAEA,UAAU,YAAiD;AACzD,UAAM,UAAU,KAAK;AACrB,QAAI,CAAC,WAAW,uBAAuB,YAAY,OAAO,KAAK,GAAG;AAChE,WAAK,cAAc;AACnB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACF;AAEA,SAAS,oBAAoB,OAA+B;AAC1D,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,cAAe,MAAoC;AACzD,SAAO,OAAO,gBAAgB,WAAW,cAAc;AACzD;AAEA,SAAS,uBACP,MACA,OACQ;AACR,QAAM,aAAa,OAAO,KAAK,UAAU,IAAI,OAAO,MAAM,UAAU;AACpE,MAAI,eAAe,GAAI,QAAO,aAAa,KAAK,IAAI;AACpD,QAAM,kBAAkB,OAAO,KAAK,UAAU,IAAI,OAAO,MAAM,UAAU;AACzE,SAAO,oBAAoB,KAAK,IAAI,kBAAkB,KAAK,IAAI;AACjE;AAEA,SAAS,qBAAqB,OAA+B;AAC3D,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,QAAS,MAA8B;AAC7C,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,UAAM,UAAW,MAAgC;AACjD,QAAI,WAAW,OAAO,YAAY,UAAU;AAC1C,YAAMA,UAAS,+BAA+B;AAAA,QAC3C,QAA0C;AAAA,MAC7C;AACA,UAAIA,QAAO,QAAS,QAAOA,QAAO;AAAA,IACpC;AAAA,EACF;AACA,QAAM,SAAU,MAAgC;AAChD,QAAM,SAAS,+BAA+B,UAAU,MAAM;AAC9D,SAAO,OAAO,UAAU,OAAO,OAAO;AACxC;AAEO,SAAS,4BACd,SACuB;AACvB,SAAO,IAAI,sBAAsB,OAAO;AAC1C;","names":["parsed"]}
|
|
1
|
+
{"version":3,"sources":["../src/accounts.ts"],"sourcesContent":["import {\n BeginManagedAuthLoginTransactionRequest,\n BootstrapManagedAuthSessionSetRequest,\n CancelManagedAuthLoginTransactionRequest,\n CompleteManagedAuthEmailPasswordTransactionRequest,\n CompleteManagedAuthLoginTransactionResponse,\n LogoutManagedAuthLoginSlotRequest,\n LogoutManagedAuthSessionSetRequest,\n MANAGED_AUTH_SESSION_SET_API_CONTRACT_HEADER,\n MANAGED_AUTH_SESSION_SET_API_CONTRACT_REVISION,\n ManagedAuthDeepLinkResolution,\n ManagedAuthLoginTransaction,\n ManagedAuthLogoutAllReceipt,\n ManagedAuthSessionSetProjection,\n ManagedAuthSessionSetErrorCode,\n ResolveManagedAuthDeepLinkRequest,\n SelectManagedAuthLoginSlotRequest,\n StartManagedAuthSocialTransactionRequest,\n StartManagedAuthSocialTransactionResponse,\n type BeginManagedAuthLoginTransactionRequest as BeginManagedAuthLoginTransactionRequestType,\n type BootstrapManagedAuthSessionSetRequest as BootstrapManagedAuthSessionSetRequestType,\n type CancelManagedAuthLoginTransactionRequest as CancelManagedAuthLoginTransactionRequestType,\n type CompleteManagedAuthEmailPasswordTransactionRequest as CompleteManagedAuthEmailPasswordTransactionRequestType,\n type CompleteManagedAuthLoginTransactionResponse as CompleteManagedAuthLoginTransactionResponseType,\n type LogoutManagedAuthLoginSlotRequest as LogoutManagedAuthLoginSlotRequestType,\n type LogoutManagedAuthSessionSetRequest as LogoutManagedAuthSessionSetRequestType,\n type ManagedAuthDeepLinkResolution as ManagedAuthDeepLinkResolutionType,\n type ManagedAuthLoginTransaction as ManagedAuthLoginTransactionType,\n type ManagedAuthLogoutAllReceipt as ManagedAuthLogoutAllReceiptType,\n type ManagedAuthSessionSetProjection as ManagedAuthSessionSetProjectionType,\n type ResolveManagedAuthDeepLinkRequest as ResolveManagedAuthDeepLinkRequestType,\n type SelectManagedAuthLoginSlotRequest as SelectManagedAuthLoginSlotRequestType,\n type StartManagedAuthSocialTransactionRequest as StartManagedAuthSocialTransactionRequestType,\n type StartManagedAuthSocialTransactionResponse as StartManagedAuthSocialTransactionResponseType,\n} from \"@opengeni/contracts/managed-auth-session-sets\";\nimport type { z } from \"zod\";\n\nexport type BrowserAccountsFetch = (\n input: string | URL | Request,\n init?: RequestInit,\n) => Promise<Response>;\n\nexport type BrowserAccountsClientOptions = {\n baseUrl: string;\n fetch?: BrowserAccountsFetch;\n};\n\nexport interface BrowserAccountsClientLike {\n getSessionSet(): Promise<ManagedAuthSessionSetProjectionType>;\n reconcileSessionSetAuthority(): Promise<ManagedAuthSessionSetProjectionType>;\n bootstrapSessionSet(\n request: BootstrapManagedAuthSessionSetRequestType,\n ): Promise<ManagedAuthSessionSetProjectionType>;\n beginLoginTransaction(\n request: BeginManagedAuthLoginTransactionRequestType,\n ): Promise<ManagedAuthLoginTransactionType>;\n completeEmailPasswordTransaction(\n request: CompleteManagedAuthEmailPasswordTransactionRequestType,\n ): Promise<CompleteManagedAuthLoginTransactionResponseType>;\n startSocialTransaction?(\n request: StartManagedAuthSocialTransactionRequestType,\n ): Promise<StartManagedAuthSocialTransactionResponseType>;\n cancelLoginTransaction(\n request: CancelManagedAuthLoginTransactionRequestType,\n ): Promise<ManagedAuthSessionSetProjectionType>;\n selectLoginSlot(\n request: SelectManagedAuthLoginSlotRequestType,\n ): Promise<ManagedAuthSessionSetProjectionType>;\n logoutLoginSlot(\n request: LogoutManagedAuthLoginSlotRequestType,\n ): Promise<ManagedAuthSessionSetProjectionType>;\n logoutSessionSet(\n request: LogoutManagedAuthSessionSetRequestType,\n ): Promise<ManagedAuthLogoutAllReceiptType>;\n resolveDeepLink(\n request: ResolveManagedAuthDeepLinkRequestType,\n ): Promise<ManagedAuthDeepLinkResolutionType>;\n}\n\nexport class BrowserAccountsApiError extends Error {\n readonly name = \"BrowserAccountsApiError\";\n constructor(\n readonly status: number,\n readonly code: string,\n options?: { cause?: unknown },\n ) {\n super(code, options?.cause === undefined ? undefined : { cause: options.cause });\n }\n}\n\nexport class BrowserAccountsClient implements BrowserAccountsClientLike {\n readonly #baseUrl: string;\n readonly #fetch: BrowserAccountsFetch;\n // CSRF is generation-bound. Exact idempotent replay must retain the\n // admission envelope even after an authority reread observes a newer clock.\n readonly #mutationAdmissions = new Map<\n string,\n { signature: string; csrfToken: string; actorEpoch: string }\n >();\n #projection: ManagedAuthSessionSetProjectionType | null = null;\n #authorityReadEpoch = 0;\n #authorityResetPending = false;\n #authorityReconciliation: Promise<ManagedAuthSessionSetProjectionType> | null = null;\n\n constructor(options: BrowserAccountsClientOptions) {\n this.#baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.#fetch = options.fetch ?? ((input, init) => fetch(input, init));\n }\n\n async getSessionSet(): Promise<ManagedAuthSessionSetProjectionType> {\n if (this.#authorityResetPending || this.#authorityReconciliation) {\n return await this.reconcileSessionSetAuthority();\n }\n const readEpoch = this.#authorityReadEpoch;\n const received = await this.#readSessionSet();\n if (readEpoch !== this.#authorityReadEpoch) {\n return this.#authorityReconciliation\n ? await this.#authorityReconciliation\n : (this.#projection ?? (await this.getSessionSet()));\n }\n return this.#remember(received);\n }\n\n async reconcileSessionSetAuthority(): Promise<ManagedAuthSessionSetProjectionType> {\n if (this.#authorityReconciliation) return await this.#authorityReconciliation;\n const previous = this.#projection;\n const readEpoch = ++this.#authorityReadEpoch;\n // Explicit reconciliation is an authority-invalidation boundary, not an\n // ordinary monotonic refresh. Old operation admissions must never cross it,\n // and mutations stay closed until two sequential reads reach a decision.\n this.#authorityResetPending = true;\n this.#mutationAdmissions.clear();\n const reconciliation = this.#reconcileSessionSetAuthority(previous, readEpoch);\n this.#authorityReconciliation = reconciliation;\n try {\n const accepted = await reconciliation;\n if (readEpoch === this.#authorityReadEpoch) this.#authorityResetPending = false;\n return accepted;\n } finally {\n if (this.#authorityReconciliation === reconciliation) {\n this.#authorityReconciliation = null;\n }\n }\n }\n\n async bootstrapSessionSet(request: BootstrapManagedAuthSessionSetRequestType) {\n return this.#projectionResponse(\n \"POST\",\n \"/v1/auth/session-set/bootstrap\",\n BootstrapManagedAuthSessionSetRequest,\n request,\n );\n }\n\n async beginLoginTransaction(request: BeginManagedAuthLoginTransactionRequestType) {\n return ManagedAuthLoginTransaction.parse(\n await this.#mutation(\n \"POST\",\n \"/v1/auth/session-set/transactions\",\n BeginManagedAuthLoginTransactionRequest,\n request,\n ),\n );\n }\n\n async completeEmailPasswordTransaction(\n request: CompleteManagedAuthEmailPasswordTransactionRequestType,\n ) {\n const authorityReadEpoch = this.#authorityReadEpoch;\n const response = CompleteManagedAuthLoginTransactionResponse.parse(\n await this.#mutation(\n \"POST\",\n \"/v1/auth/session-set/transactions/email-password\",\n CompleteManagedAuthEmailPasswordTransactionRequest,\n request,\n ),\n );\n if (authorityReadEpoch !== this.#authorityReadEpoch) {\n return { ...response, projection: await this.getSessionSet() };\n }\n const remembered = this.#remember(response.projection);\n if (remembered !== response.projection) {\n return { ...response, projection: await this.getSessionSet() };\n }\n return response;\n }\n\n async startSocialTransaction(request: StartManagedAuthSocialTransactionRequestType) {\n return StartManagedAuthSocialTransactionResponse.parse(\n await this.#mutation(\n \"POST\",\n \"/v1/auth/session-set/transactions/social\",\n StartManagedAuthSocialTransactionRequest,\n request,\n ),\n );\n }\n\n async cancelLoginTransaction(request: CancelManagedAuthLoginTransactionRequestType) {\n return this.#projectionResponse(\n \"DELETE\",\n `/v1/auth/session-set/transactions/${encodeURIComponent(request.transactionId)}`,\n CancelManagedAuthLoginTransactionRequest,\n request,\n );\n }\n\n async selectLoginSlot(request: SelectManagedAuthLoginSlotRequestType) {\n return this.#projectionResponse(\n \"POST\",\n \"/v1/auth/session-set/select\",\n SelectManagedAuthLoginSlotRequest,\n request,\n );\n }\n\n async logoutLoginSlot(request: LogoutManagedAuthLoginSlotRequestType) {\n return this.#projectionResponse(\n \"POST\",\n \"/v1/auth/session-set/logout-one\",\n LogoutManagedAuthLoginSlotRequest,\n request,\n );\n }\n\n async logoutSessionSet(request: LogoutManagedAuthSessionSetRequestType) {\n const receipt = ManagedAuthLogoutAllReceipt.parse(\n await this.#mutation(\n \"POST\",\n \"/v1/auth/session-set/logout-all\",\n LogoutManagedAuthSessionSetRequest,\n request,\n ),\n );\n this.#authorityReadEpoch += 1;\n this.#authorityResetPending = false;\n this.#projection = null;\n this.#mutationAdmissions.clear();\n return receipt;\n }\n\n async resolveDeepLink(request: ResolveManagedAuthDeepLinkRequestType) {\n return ManagedAuthDeepLinkResolution.parse(\n await this.#mutation(\n \"POST\",\n \"/v1/auth/session-set/deep-link/resolve\",\n ResolveManagedAuthDeepLinkRequest,\n request,\n ),\n );\n }\n\n async #projectionResponse<S extends z.ZodType>(\n method: \"POST\" | \"DELETE\",\n path: string,\n schema: S,\n request: z.infer<S>,\n ): Promise<ManagedAuthSessionSetProjectionType> {\n const authorityReadEpoch = this.#authorityReadEpoch;\n const received = ManagedAuthSessionSetProjection.parse(\n await this.#mutation(method, path, schema, request),\n );\n if (authorityReadEpoch !== this.#authorityReadEpoch) return await this.getSessionSet();\n const remembered = this.#remember(received);\n // An exact replay may legitimately return its older durable receipt after\n // another tab has advanced this in-memory client. Reconcile from authority\n // instead of presenting that receipt as the current actor projection.\n return remembered === received ? received : await this.getSessionSet();\n }\n\n async #mutation<S extends z.ZodType>(\n method: \"POST\" | \"DELETE\",\n path: string,\n schema: S,\n request: z.infer<S>,\n ): Promise<unknown> {\n if (this.#authorityResetPending) {\n throw new BrowserAccountsApiError(409, \"actor_change_required\");\n }\n const body = schema.parse(request);\n const projection = this.#projection;\n if (!projection) throw new BrowserAccountsApiError(409, \"session_set_projection_required\");\n const operationId = mutationOperationId(body);\n const signature = operationId ? JSON.stringify([method, path, body]) : null;\n const retained = operationId ? this.#mutationAdmissions.get(operationId) : null;\n if (retained && retained.signature !== signature) {\n throw new BrowserAccountsApiError(409, \"operation_reused\");\n }\n const admission =\n retained ??\n ({\n signature: signature ?? \"\",\n csrfToken: projection.csrfToken,\n actorEpoch: projection.actorEpoch,\n } as const);\n if (operationId && !retained) {\n this.#mutationAdmissions.set(operationId, admission);\n if (this.#mutationAdmissions.size > 64) {\n this.#mutationAdmissions.delete(this.#mutationAdmissions.keys().next().value!);\n }\n }\n return await this.#request(method, path, body, {\n \"x-opengeni-session-csrf\": admission.csrfToken,\n \"x-opengeni-actor-epoch\": admission.actorEpoch,\n });\n }\n\n async #request(\n method: \"GET\" | \"POST\" | \"DELETE\",\n path: string,\n body?: unknown,\n headers: Record<string, string> = {},\n ): Promise<unknown> {\n let response: Response;\n try {\n response = await this.#fetch(`${this.#baseUrl}${path}`, {\n method,\n credentials: \"include\",\n headers: {\n accept: \"application/json\",\n [MANAGED_AUTH_SESSION_SET_API_CONTRACT_HEADER]:\n MANAGED_AUTH_SESSION_SET_API_CONTRACT_REVISION,\n ...(body === undefined ? {} : { \"content-type\": \"application/json\" }),\n ...headers,\n },\n ...(body === undefined ? {} : { body: JSON.stringify(body) }),\n });\n } catch (cause) {\n if (method !== \"GET\") {\n throw new BrowserAccountsApiError(503, \"operation_outcome_unknown\", { cause });\n }\n throw cause;\n }\n let parsed = true;\n const value = await response.json().catch(() => {\n parsed = false;\n return null;\n });\n if (!response.ok) {\n const code = managedAuthErrorCode(value) ?? `http_${response.status}`;\n throw new BrowserAccountsApiError(response.status, code);\n }\n if (!parsed && method !== \"GET\") {\n throw new BrowserAccountsApiError(503, \"operation_outcome_unknown\");\n }\n return value;\n }\n\n async #readSessionSet(): Promise<ManagedAuthSessionSetProjectionType> {\n return ManagedAuthSessionSetProjection.parse(\n await this.#request(\"GET\", \"/v1/auth/session-set\"),\n );\n }\n\n async #reconcileSessionSetAuthority(\n previous: ManagedAuthSessionSetProjectionType | null,\n readEpoch: number,\n ): Promise<ManagedAuthSessionSetProjectionType> {\n const first = await this.#readSessionSet();\n const second = await this.#readSessionSet();\n if (readEpoch !== this.#authorityReadEpoch) {\n return await this.#reconcileSessionSetAuthority(this.#projection, this.#authorityReadEpoch);\n }\n if (this.#projection !== previous) return this.#remember(second);\n if (!previous || compareProjectionClock(second, previous) >= 0) {\n return this.#remember(second);\n }\n if (compareProjectionClock(first, previous) < 0 && compareProjectionClock(second, first) >= 0) {\n // Same-authority clocks never decrease. Two sequential no-store reads\n // that both remain below the remembered clock therefore prove that the\n // HttpOnly authority rotated; the second may already have advanced from\n // the fresh authority's initial 1/1 projection.\n this.#projection = second;\n this.#mutationAdmissions.clear();\n return second;\n }\n return previous;\n }\n\n #remember(projection: ManagedAuthSessionSetProjectionType) {\n const current = this.#projection;\n if (!current || compareProjectionClock(projection, current) >= 0) {\n this.#projection = projection;\n return projection;\n }\n return current;\n }\n}\n\nfunction mutationOperationId(value: unknown): string | null {\n if (!value || typeof value !== \"object\") return null;\n const operationId = (value as { operationId?: unknown }).operationId;\n return typeof operationId === \"string\" ? operationId : null;\n}\n\nfunction compareProjectionClock(\n left: Pick<ManagedAuthSessionSetProjectionType, \"actorEpoch\" | \"generation\">,\n right: Pick<ManagedAuthSessionSetProjectionType, \"actorEpoch\" | \"generation\">,\n): number {\n const actorDelta = BigInt(left.actorEpoch) - BigInt(right.actorEpoch);\n if (actorDelta !== 0n) return actorDelta > 0n ? 1 : -1;\n const generationDelta = BigInt(left.generation) - BigInt(right.generation);\n return generationDelta === 0n ? 0 : generationDelta > 0n ? 1 : -1;\n}\n\nfunction managedAuthErrorCode(value: unknown): string | null {\n if (!value || typeof value !== \"object\") return null;\n const error = (value as { error?: unknown }).error;\n if (error && typeof error === \"object\") {\n const details = (error as { details?: unknown }).details;\n if (details && typeof details === \"object\") {\n const parsed = ManagedAuthSessionSetErrorCode.safeParse(\n (details as { managedAuthCode?: unknown }).managedAuthCode,\n );\n if (parsed.success) return parsed.data;\n }\n }\n const legacy = (value as { message?: unknown }).message;\n const parsed = ManagedAuthSessionSetErrorCode.safeParse(legacy);\n return parsed.success ? parsed.data : null;\n}\n\nexport function createBrowserAccountsClient(\n options: BrowserAccountsClientOptions,\n): BrowserAccountsClient {\n return new BrowserAccountsClient(options);\n}\n\nexport type {\n BeginManagedAuthLoginTransactionRequestType as BeginManagedAuthLoginTransactionRequest,\n BootstrapManagedAuthSessionSetRequestType as BootstrapManagedAuthSessionSetRequest,\n CancelManagedAuthLoginTransactionRequestType as CancelManagedAuthLoginTransactionRequest,\n CompleteManagedAuthEmailPasswordTransactionRequestType as CompleteManagedAuthEmailPasswordTransactionRequest,\n CompleteManagedAuthLoginTransactionResponseType as CompleteManagedAuthLoginTransactionResponse,\n LogoutManagedAuthLoginSlotRequestType as LogoutManagedAuthLoginSlotRequest,\n LogoutManagedAuthSessionSetRequestType as LogoutManagedAuthSessionSetRequest,\n ManagedAuthDeepLinkResolutionType as ManagedAuthDeepLinkResolution,\n ManagedAuthLoginTransactionType as ManagedAuthLoginTransaction,\n ManagedAuthLogoutAllReceiptType as ManagedAuthLogoutAllReceipt,\n ManagedAuthSessionSetProjectionType as ManagedAuthSessionSetProjection,\n ResolveManagedAuthDeepLinkRequestType as ResolveManagedAuthDeepLinkRequest,\n SelectManagedAuthLoginSlotRequestType as SelectManagedAuthLoginSlotRequest,\n};\n"],"mappings":"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","names":["parsed"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
/** Any web-standard handler: `createSessionProxyHandler`, `createChatHandler`, or your own. */
|
|
3
|
+
export type WebHandler = (request: Request) => Promise<Response>;
|
|
4
|
+
/** Express/Connect request: Node's `IncomingMessage` plus what those frameworks add. */
|
|
5
|
+
export type NodeRequestLike = IncomingMessage & {
|
|
6
|
+
originalUrl?: string | undefined;
|
|
7
|
+
/** Set when a body parser (for example `express.json()`) already consumed the body. */
|
|
8
|
+
body?: unknown;
|
|
9
|
+
protocol?: string | undefined;
|
|
10
|
+
};
|
|
11
|
+
export type NodeMiddleware = (request: NodeRequestLike, response: ServerResponse, next?: (error?: unknown) => void) => void;
|
|
12
|
+
export type NodeMiddlewareOptions = {
|
|
13
|
+
/** Origin used to build the request URL. Defaults to the Host header over http(s). */
|
|
14
|
+
origin?: string | undefined;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Expose a web-standard handler as Express/Connect (or plain `node:http`)
|
|
18
|
+
* middleware. Mount it with the same path the browser client uses:
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* app.use("/api/opengeni", toNodeMiddleware(handler));
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* The full original URL is forwarded (Express strips the mount path from
|
|
25
|
+
* `req.url`), bodies stream through unless a body parser already consumed
|
|
26
|
+
* them, responses (including SSE) are written as they arrive, and a client
|
|
27
|
+
* disconnect aborts the request signal.
|
|
28
|
+
*/
|
|
29
|
+
export declare function toNodeMiddleware(handler: WebHandler, options?: NodeMiddlewareOptions): NodeMiddleware;
|
|
30
|
+
/** Express/Connect naming alias. */
|
|
31
|
+
export declare const toExpressMiddleware: typeof toNodeMiddleware;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// src/adapters/express.ts
|
|
2
|
+
import { Readable } from "stream";
|
|
3
|
+
function toNodeMiddleware(handler, options = {}) {
|
|
4
|
+
return (request, response, next) => {
|
|
5
|
+
void (async () => {
|
|
6
|
+
const abort = new AbortController();
|
|
7
|
+
response.once("close", () => {
|
|
8
|
+
if (!response.writableFinished) abort.abort();
|
|
9
|
+
});
|
|
10
|
+
const web = await handler(toWebRequest(request, abort.signal, options));
|
|
11
|
+
await writeWebResponse(web, response);
|
|
12
|
+
})().catch((error) => {
|
|
13
|
+
if (next) next(error);
|
|
14
|
+
else if (!response.headersSent) {
|
|
15
|
+
response.statusCode = 500;
|
|
16
|
+
response.end();
|
|
17
|
+
} else response.destroy(error instanceof Error ? error : void 0);
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
var toExpressMiddleware = toNodeMiddleware;
|
|
22
|
+
function toWebRequest(request, signal, options) {
|
|
23
|
+
const encrypted = request.socket?.encrypted === true;
|
|
24
|
+
const origin = options.origin?.replace(/\/+$/, "") ?? `${request.protocol ?? (encrypted ? "https" : "http")}://${request.headers.host ?? "localhost"}`;
|
|
25
|
+
const url = new URL(request.originalUrl ?? request.url ?? "/", origin);
|
|
26
|
+
const headers = new Headers();
|
|
27
|
+
for (const [name, value] of Object.entries(request.headers)) {
|
|
28
|
+
if (value === void 0) continue;
|
|
29
|
+
if (Array.isArray(value)) for (const item of value) headers.append(name, item);
|
|
30
|
+
else headers.set(name, value);
|
|
31
|
+
}
|
|
32
|
+
const method = request.method ?? "GET";
|
|
33
|
+
let body;
|
|
34
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
35
|
+
if (request.body !== void 0 && (request.readableEnded || request.complete)) {
|
|
36
|
+
body = typeof request.body === "string" || request.body instanceof Uint8Array ? request.body : JSON.stringify(request.body);
|
|
37
|
+
headers.delete("content-length");
|
|
38
|
+
} else {
|
|
39
|
+
body = Readable.toWeb(request);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return new Request(url, {
|
|
43
|
+
method,
|
|
44
|
+
headers,
|
|
45
|
+
signal,
|
|
46
|
+
...body !== void 0 ? { body, duplex: "half" } : {}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async function writeWebResponse(web, response) {
|
|
50
|
+
response.statusCode = web.status;
|
|
51
|
+
const cookies = web.headers.getSetCookie?.() ?? [];
|
|
52
|
+
web.headers.forEach((value, name) => {
|
|
53
|
+
if (name.toLowerCase() !== "set-cookie") response.setHeader(name, value);
|
|
54
|
+
});
|
|
55
|
+
if (cookies.length > 0) response.setHeader("set-cookie", cookies);
|
|
56
|
+
if (!web.body) {
|
|
57
|
+
response.end();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
response.flushHeaders();
|
|
61
|
+
const reader = web.body.getReader();
|
|
62
|
+
response.once("close", () => {
|
|
63
|
+
void reader.cancel().catch(() => void 0);
|
|
64
|
+
});
|
|
65
|
+
for (; ; ) {
|
|
66
|
+
const { done, value } = await reader.read();
|
|
67
|
+
if (done) break;
|
|
68
|
+
if (!response.write(value)) {
|
|
69
|
+
await new Promise((resolve) => {
|
|
70
|
+
const settle = () => {
|
|
71
|
+
response.off("drain", settle);
|
|
72
|
+
response.off("close", settle);
|
|
73
|
+
resolve();
|
|
74
|
+
};
|
|
75
|
+
response.once("drain", settle);
|
|
76
|
+
response.once("close", settle);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (response.destroyed) break;
|
|
80
|
+
}
|
|
81
|
+
response.end();
|
|
82
|
+
}
|
|
83
|
+
export {
|
|
84
|
+
toExpressMiddleware,
|
|
85
|
+
toNodeMiddleware
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=express.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/express.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { Readable } from \"node:stream\";\n\n/** Any web-standard handler: `createSessionProxyHandler`, `createChatHandler`, or your own. */\nexport type WebHandler = (request: Request) => Promise<Response>;\n\n/** Express/Connect request: Node's `IncomingMessage` plus what those frameworks add. */\nexport type NodeRequestLike = IncomingMessage & {\n originalUrl?: string | undefined;\n /** Set when a body parser (for example `express.json()`) already consumed the body. */\n body?: unknown;\n protocol?: string | undefined;\n};\n\nexport type NodeMiddleware = (\n request: NodeRequestLike,\n response: ServerResponse,\n next?: (error?: unknown) => void,\n) => void;\n\nexport type NodeMiddlewareOptions = {\n /** Origin used to build the request URL. Defaults to the Host header over http(s). */\n origin?: string | undefined;\n};\n\n/**\n * Expose a web-standard handler as Express/Connect (or plain `node:http`)\n * middleware. Mount it with the same path the browser client uses:\n *\n * ```ts\n * app.use(\"/api/opengeni\", toNodeMiddleware(handler));\n * ```\n *\n * The full original URL is forwarded (Express strips the mount path from\n * `req.url`), bodies stream through unless a body parser already consumed\n * them, responses (including SSE) are written as they arrive, and a client\n * disconnect aborts the request signal.\n */\nexport function toNodeMiddleware(\n handler: WebHandler,\n options: NodeMiddlewareOptions = {},\n): NodeMiddleware {\n return (request, response, next) => {\n void (async () => {\n const abort = new AbortController();\n response.once(\"close\", () => {\n if (!response.writableFinished) abort.abort();\n });\n const web = await handler(toWebRequest(request, abort.signal, options));\n await writeWebResponse(web, response);\n })().catch((error: unknown) => {\n if (next) next(error);\n else if (!response.headersSent) {\n response.statusCode = 500;\n response.end();\n } else response.destroy(error instanceof Error ? error : undefined);\n });\n };\n}\n\n/** Express/Connect naming alias. */\nexport const toExpressMiddleware = toNodeMiddleware;\n\nfunction toWebRequest(\n request: NodeRequestLike,\n signal: AbortSignal,\n options: NodeMiddlewareOptions,\n): Request {\n const encrypted = (request.socket as { encrypted?: boolean } | undefined)?.encrypted === true;\n const origin =\n options.origin?.replace(/\\/+$/, \"\") ??\n `${request.protocol ?? (encrypted ? \"https\" : \"http\")}://${request.headers.host ?? \"localhost\"}`;\n const url = new URL(request.originalUrl ?? request.url ?? \"/\", origin);\n const headers = new Headers();\n for (const [name, value] of Object.entries(request.headers)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) for (const item of value) headers.append(name, item);\n else headers.set(name, value);\n }\n const method = request.method ?? \"GET\";\n let body: BodyInit | undefined;\n if (method !== \"GET\" && method !== \"HEAD\") {\n if (request.body !== undefined && (request.readableEnded || request.complete)) {\n // A body parser consumed the stream: forward what it parsed.\n body =\n typeof request.body === \"string\" || request.body instanceof Uint8Array\n ? (request.body as string | Uint8Array<ArrayBuffer>)\n : JSON.stringify(request.body);\n headers.delete(\"content-length\");\n } else {\n body = Readable.toWeb(request) as unknown as ReadableStream<Uint8Array>;\n }\n }\n return new Request(url, {\n method,\n headers,\n signal,\n ...(body !== undefined ? { body, duplex: \"half\" } : {}),\n } as RequestInit);\n}\n\nasync function writeWebResponse(web: Response, response: ServerResponse): Promise<void> {\n response.statusCode = web.status;\n const cookies = web.headers.getSetCookie?.() ?? [];\n web.headers.forEach((value, name) => {\n if (name.toLowerCase() !== \"set-cookie\") response.setHeader(name, value);\n });\n if (cookies.length > 0) response.setHeader(\"set-cookie\", cookies);\n if (!web.body) {\n response.end();\n return;\n }\n // Send headers now so SSE clients see the stream open before the first event.\n response.flushHeaders();\n const reader = web.body.getReader();\n response.once(\"close\", () => {\n void reader.cancel().catch(() => undefined);\n });\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!response.write(value)) {\n await new Promise<void>((resolve) => {\n const settle = () => {\n response.off(\"drain\", settle);\n response.off(\"close\", settle);\n resolve();\n };\n response.once(\"drain\", settle);\n response.once(\"close\", settle);\n });\n }\n if (response.destroyed) break;\n }\n response.end();\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Any web-standard handler: `createSessionProxyHandler`, `createChatHandler`, or your own. */
|
|
2
|
+
export type WebHandler = (request: Request) => Promise<Response>;
|
|
3
|
+
/** The part of Hono's context this adapter uses (no Hono dependency). */
|
|
4
|
+
export type HonoContextLike = {
|
|
5
|
+
req: {
|
|
6
|
+
raw: Request;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Expose a web-standard handler as a Hono handler:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* app.all("/api/opengeni/*", toHonoHandler(handler));
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare function toHonoHandler(handler: WebHandler): (context: HonoContextLike) => Promise<Response>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/hono.ts"],"sourcesContent":["/** Any web-standard handler: `createSessionProxyHandler`, `createChatHandler`, or your own. */\nexport type WebHandler = (request: Request) => Promise<Response>;\n\n/** The part of Hono's context this adapter uses (no Hono dependency). */\nexport type HonoContextLike = { req: { raw: Request } };\n\n/**\n * Expose a web-standard handler as a Hono handler:\n *\n * ```ts\n * app.all(\"/api/opengeni/*\", toHonoHandler(handler));\n * ```\n */\nexport function toHonoHandler(\n handler: WebHandler,\n): (context: HonoContextLike) => Promise<Response> {\n return async (context) => await handler(context.req.raw);\n}\n"],"mappings":";AAaO,SAAS,cACd,SACiD;AACjD,SAAO,OAAO,YAAY,MAAM,QAAQ,QAAQ,IAAI,GAAG;AACzD;","names":[]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createSessionProxyHandler, type SessionProxyHandlerOptions } from "../session-proxy.js";
|
|
2
|
+
/** Any web-standard handler: `createSessionProxyHandler`, `createChatHandler`, or your own. */
|
|
3
|
+
export type WebHandler = (request: Request) => Promise<Response>;
|
|
4
|
+
/** Route handlers for a Next.js App Router catch-all route. */
|
|
5
|
+
export type NextRouteHandlers = {
|
|
6
|
+
GET: WebHandler;
|
|
7
|
+
POST: WebHandler;
|
|
8
|
+
PUT: WebHandler;
|
|
9
|
+
PATCH: WebHandler;
|
|
10
|
+
DELETE: WebHandler;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Expose a web-standard handler as Next.js App Router route handlers.
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* // app/api/opengeni/[...path]/route.ts
|
|
17
|
+
* export const dynamic = "force-dynamic";
|
|
18
|
+
* export const { GET, POST, PUT, PATCH, DELETE } = toNextRouteHandlers(handler);
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* Next passes the full request URL (including any `basePath`), so the proxy's
|
|
22
|
+
* own `/v1/` detection routes it; responses, including SSE, stream as-is.
|
|
23
|
+
*/
|
|
24
|
+
export declare function toNextRouteHandlers(handler: WebHandler): NextRouteHandlers;
|
|
25
|
+
/**
|
|
26
|
+
* `createSessionProxyHandler` as Next.js App Router route handlers, for
|
|
27
|
+
* `app/<mount>/[...path]/route.ts`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createSessionProxyRoute(target: Parameters<typeof createSessionProxyHandler>[0], options: SessionProxyHandlerOptions): NextRouteHandlers;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createSessionProxyHandler
|
|
3
|
+
} from "../chunk-6HZGAIUE.js";
|
|
4
|
+
import "../chunk-UM4ZXXJR.js";
|
|
5
|
+
import "../chunk-QGEIL4TR.js";
|
|
6
|
+
import "../chunk-4AHR4KGA.js";
|
|
7
|
+
import "../chunk-KADZ5PGG.js";
|
|
8
|
+
import "../chunk-7EWT3PV6.js";
|
|
9
|
+
|
|
10
|
+
// src/adapters/next.ts
|
|
11
|
+
function toNextRouteHandlers(handler) {
|
|
12
|
+
const route = async (request) => await handler(request);
|
|
13
|
+
return { GET: route, POST: route, PUT: route, PATCH: route, DELETE: route };
|
|
14
|
+
}
|
|
15
|
+
function createSessionProxyRoute(target, options) {
|
|
16
|
+
return toNextRouteHandlers(createSessionProxyHandler(target, options));
|
|
17
|
+
}
|
|
18
|
+
export {
|
|
19
|
+
createSessionProxyRoute,
|
|
20
|
+
toNextRouteHandlers
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=next.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/next.ts"],"sourcesContent":["import { createSessionProxyHandler, type SessionProxyHandlerOptions } from \"../session-proxy\";\n\n/** Any web-standard handler: `createSessionProxyHandler`, `createChatHandler`, or your own. */\nexport type WebHandler = (request: Request) => Promise<Response>;\n\n/** Route handlers for a Next.js App Router catch-all route. */\nexport type NextRouteHandlers = {\n GET: WebHandler;\n POST: WebHandler;\n PUT: WebHandler;\n PATCH: WebHandler;\n DELETE: WebHandler;\n};\n\n/**\n * Expose a web-standard handler as Next.js App Router route handlers.\n *\n * ```ts\n * // app/api/opengeni/[...path]/route.ts\n * export const dynamic = \"force-dynamic\";\n * export const { GET, POST, PUT, PATCH, DELETE } = toNextRouteHandlers(handler);\n * ```\n *\n * Next passes the full request URL (including any `basePath`), so the proxy's\n * own `/v1/` detection routes it; responses, including SSE, stream as-is.\n */\nexport function toNextRouteHandlers(handler: WebHandler): NextRouteHandlers {\n // Next calls route handlers with (request, context); only the request matters.\n const route: WebHandler = async (request) => await handler(request);\n return { GET: route, POST: route, PUT: route, PATCH: route, DELETE: route };\n}\n\n/**\n * `createSessionProxyHandler` as Next.js App Router route handlers, for\n * `app/<mount>/[...path]/route.ts`.\n */\nexport function createSessionProxyRoute(\n target: Parameters<typeof createSessionProxyHandler>[0],\n options: SessionProxyHandlerOptions,\n): NextRouteHandlers {\n return toNextRouteHandlers(createSessionProxyHandler(target, options));\n}\n"],"mappings":";;;;;;;;;;AA0BO,SAAS,oBAAoB,SAAwC;AAE1E,QAAM,QAAoB,OAAO,YAAY,MAAM,QAAQ,OAAO;AAClE,SAAO,EAAE,KAAK,OAAO,MAAM,OAAO,KAAK,OAAO,OAAO,OAAO,QAAQ,MAAM;AAC5E;AAMO,SAAS,wBACd,QACA,SACmB;AACnB,SAAO,oBAAoB,0BAA0B,QAAQ,OAAO,CAAC;AACvE;","names":[]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// ../contracts/src/allowance-refusal.ts
|
|
2
|
+
var MESSAGE_MAX_UTF8_BYTES = 1024;
|
|
3
|
+
function record(value) {
|
|
4
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
5
|
+
}
|
|
6
|
+
function resetTimestamp(value) {
|
|
7
|
+
if (value === null) return true;
|
|
8
|
+
if (typeof value !== "string" || value.length > 64) return false;
|
|
9
|
+
const parts = /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])T(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.exec(
|
|
10
|
+
value
|
|
11
|
+
);
|
|
12
|
+
if (!parts || !Number.isFinite(Date.parse(value))) return false;
|
|
13
|
+
const year = Number(parts[1]);
|
|
14
|
+
const month = Number(parts[2]);
|
|
15
|
+
const day = Number(parts[3]);
|
|
16
|
+
const leapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
17
|
+
const days = [31, leapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
18
|
+
return day <= days[month - 1];
|
|
19
|
+
}
|
|
20
|
+
function parseAllowanceExhaustedRefusal(value) {
|
|
21
|
+
const source = record(value);
|
|
22
|
+
if (source?.code !== "allowance_exhausted") return null;
|
|
23
|
+
const fields = record(source.details) ?? source;
|
|
24
|
+
const scope = fields.scope;
|
|
25
|
+
const subjectId = scope === "member" ? fields.subjectId : void 0;
|
|
26
|
+
if (typeof source.message !== "string" || scope !== "workspace" && scope !== "member" || !resetTimestamp(fields.resetsAt) || subjectId !== void 0 && (typeof subjectId !== "string" || subjectId.length === 0 || subjectId.length > 1024 || subjectId.includes("\0"))) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const normalized = source.message.slice(0, MESSAGE_MAX_UTF8_BYTES * 2).replace(/[\u0000-\u001f\u007f]+/g, " ").trim();
|
|
30
|
+
const bytes = new TextEncoder().encode(normalized.slice(0, MESSAGE_MAX_UTF8_BYTES * 2));
|
|
31
|
+
let end = Math.min(bytes.length, MESSAGE_MAX_UTF8_BYTES);
|
|
32
|
+
while (end > 0 && end < bytes.length && (bytes[end] & 192) === 128) end -= 1;
|
|
33
|
+
return {
|
|
34
|
+
code: "allowance_exhausted",
|
|
35
|
+
scope,
|
|
36
|
+
resetsAt: fields.resetsAt,
|
|
37
|
+
...typeof subjectId === "string" ? { subjectId } : {},
|
|
38
|
+
message: new TextDecoder().decode(bytes.slice(0, end)).trim()
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function allowanceExhaustedMessage(refusal) {
|
|
42
|
+
const remedy = refusal.scope === "workspace" ? "The workspace usage allowance is exhausted. An organization administrator or a full-access organization API key can raise the workspace ceiling or add an allowance grant." : "The member usage allowance is exhausted. A workspace administrator or a full-access organization API key can adjust the member ceiling.";
|
|
43
|
+
const reset = refusal.resetsAt === null ? "This allowance has no automatic reset." : `It resets at ${new Date(refusal.resetsAt).toISOString().slice(0, 16).replace("T", " ")} UTC.`;
|
|
44
|
+
return `${remedy} ${reset}`;
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
allowanceExhaustedMessage,
|
|
48
|
+
parseAllowanceExhaustedRefusal
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=allowance-refusal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../contracts/src/allowance-refusal.ts"],"sourcesContent":["import type { AllowanceExhaustedRefusal } from \"./usage-allowances\";\n\nconst MESSAGE_MAX_UTF8_BYTES = 1_024;\n\nfunction record(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n}\n\n/** Mirror the canonical offset datetime's calendar/time bounds without loading\n * the schema runtime into synchronous browser/React Native session projection.\n * Contract-parity tests guard this deliberately small, bounded wire leaf.\n */\nfunction resetTimestamp(value: unknown): value is string | null {\n if (value === null) return true;\n if (typeof value !== \"string\" || value.length > 64) return false;\n const parts =\n /^(\\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])T(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$/.exec(\n value,\n );\n if (!parts || !Number.isFinite(Date.parse(value))) return false;\n const year = Number(parts[1]);\n const month = Number(parts[2]);\n const day = Number(parts[3]);\n const leapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);\n const days = [31, leapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n return day <= days[month - 1]!;\n}\n\n/** Select only public refusal fields, including from an API error's details.\n * LimitDecision's allowed:false and transport/SQL diagnostics never cross out.\n */\nexport function parseAllowanceExhaustedRefusal(value: unknown): AllowanceExhaustedRefusal | null {\n const source = record(value);\n if (source?.code !== \"allowance_exhausted\") return null;\n const fields = record(source.details) ?? source;\n const scope = fields.scope;\n const subjectId = scope === \"member\" ? fields.subjectId : undefined;\n if (\n typeof source.message !== \"string\" ||\n (scope !== \"workspace\" && scope !== \"member\") ||\n !resetTimestamp(fields.resetsAt) ||\n (subjectId !== undefined &&\n (typeof subjectId !== \"string\" ||\n subjectId.length === 0 ||\n subjectId.length > 1_024 ||\n subjectId.includes(\"\\0\")))\n ) {\n return null;\n }\n const normalized = source.message\n .slice(0, MESSAGE_MAX_UTF8_BYTES * 2)\n .replace(/[\\u0000-\\u001f\\u007f]+/g, \" \")\n .trim();\n const bytes = new TextEncoder().encode(normalized.slice(0, MESSAGE_MAX_UTF8_BYTES * 2));\n let end = Math.min(bytes.length, MESSAGE_MAX_UTF8_BYTES);\n while (end > 0 && end < bytes.length && (bytes[end]! & 0xc0) === 0x80) end -= 1;\n return {\n code: \"allowance_exhausted\",\n scope,\n resetsAt: fields.resetsAt,\n ...(typeof subjectId === \"string\" ? { subjectId } : {}),\n message: new TextDecoder().decode(bytes.slice(0, end)).trim(),\n };\n}\n\n/** Bounded, fixed remedies: a ceiling is not an exhausted billing source.\n * Never interpolate arbitrary exception prose or a member identifier.\n */\nexport function allowanceExhaustedMessage(refusal: AllowanceExhaustedRefusal): string {\n const remedy =\n refusal.scope === \"workspace\"\n ? \"The workspace usage allowance is exhausted. An organization administrator or a full-access organization API key can raise the workspace ceiling or add an allowance grant.\"\n : \"The member usage allowance is exhausted. A workspace administrator or a full-access organization API key can adjust the member ceiling.\";\n const reset =\n refusal.resetsAt === null\n ? \"This allowance has no automatic reset.\"\n : `It resets at ${new Date(refusal.resetsAt).toISOString().slice(0, 16).replace(\"T\", \" \")} UTC.`;\n return `${remedy} ${reset}`;\n}\n"],"mappings":";AAEA,IAAM,yBAAyB;AAE/B,SAAS,OAAO,OAAgD;AAC9D,SAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IACrE,QACD;AACN;AAMA,SAAS,eAAe,OAAwC;AAC9D,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAAI,QAAO;AAC3D,QAAM,QACJ,sIAAsI;AAAA,IACpI;AAAA,EACF;AACF,MAAI,CAAC,SAAS,CAAC,OAAO,SAAS,KAAK,MAAM,KAAK,CAAC,EAAG,QAAO;AAC1D,QAAM,OAAO,OAAO,MAAM,CAAC,CAAC;AAC5B,QAAM,QAAQ,OAAO,MAAM,CAAC,CAAC;AAC7B,QAAM,MAAM,OAAO,MAAM,CAAC,CAAC;AAC3B,QAAM,WAAW,OAAO,MAAM,MAAM,OAAO,QAAQ,KAAK,OAAO,QAAQ;AACvE,QAAM,OAAO,CAAC,IAAI,WAAW,KAAK,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAC5E,SAAO,OAAO,KAAK,QAAQ,CAAC;AAC9B;AAKO,SAAS,+BAA+B,OAAkD;AAC/F,QAAM,SAAS,OAAO,KAAK;AAC3B,MAAI,QAAQ,SAAS,sBAAuB,QAAO;AACnD,QAAM,SAAS,OAAO,OAAO,OAAO,KAAK;AACzC,QAAM,QAAQ,OAAO;AACrB,QAAM,YAAY,UAAU,WAAW,OAAO,YAAY;AAC1D,MACE,OAAO,OAAO,YAAY,YACzB,UAAU,eAAe,UAAU,YACpC,CAAC,eAAe,OAAO,QAAQ,KAC9B,cAAc,WACZ,OAAO,cAAc,YACpB,UAAU,WAAW,KACrB,UAAU,SAAS,QACnB,UAAU,SAAS,IAAI,IAC3B;AACA,WAAO;AAAA,EACT;AACA,QAAM,aAAa,OAAO,QACvB,MAAM,GAAG,yBAAyB,CAAC,EACnC,QAAQ,2BAA2B,GAAG,EACtC,KAAK;AACR,QAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,WAAW,MAAM,GAAG,yBAAyB,CAAC,CAAC;AACtF,MAAI,MAAM,KAAK,IAAI,MAAM,QAAQ,sBAAsB;AACvD,SAAO,MAAM,KAAK,MAAM,MAAM,WAAW,MAAM,GAAG,IAAK,SAAU,IAAM,QAAO;AAC9E,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,UAAU,OAAO;AAAA,IACjB,GAAI,OAAO,cAAc,WAAW,EAAE,UAAU,IAAI,CAAC;AAAA,IACrD,SAAS,IAAI,YAAY,EAAE,OAAO,MAAM,MAAM,GAAG,GAAG,CAAC,EAAE,KAAK;AAAA,EAC9D;AACF;AAKO,SAAS,0BAA0B,SAA4C;AACpF,QAAM,SACJ,QAAQ,UAAU,cACd,+KACA;AACN,QAAM,QACJ,QAAQ,aAAa,OACjB,2CACA,gBAAgB,IAAI,KAAK,QAAQ,QAAQ,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,EAAE,QAAQ,KAAK,GAAG,CAAC;AAC7F,SAAO,GAAG,MAAM,IAAI,KAAK;AAC3B;","names":[]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** A deployment address as typed: trimmed, https by default, no trailing slash. */
|
|
2
|
+
export declare function normalizeServerUrl(input: string): string | null;
|
|
3
|
+
/** The host a person recognizes, e.g. app.opengeni.ai. */
|
|
4
|
+
export declare function serverLabel(baseUrl: string): string;
|
|
5
|
+
/** The part of a signed-in app account that link routing reads. */
|
|
6
|
+
export interface AppLinkAccount {
|
|
7
|
+
id: string;
|
|
8
|
+
/** The deployment's web origin, e.g. https://app.opengeni.ai. */
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
/** The workspace last used with this account. */
|
|
11
|
+
workspaceId: string | null;
|
|
12
|
+
/** Set when the deployment rejected the credential. */
|
|
13
|
+
signedOut?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A web link the app can show itself: a conversation or a workspace on some
|
|
17
|
+
* deployment. Everything else on the web (settings, organization, artifacts)
|
|
18
|
+
* stays in the browser.
|
|
19
|
+
*/
|
|
20
|
+
export interface AppLink {
|
|
21
|
+
/** The deployment's origin, normalized like a stored account's baseUrl. */
|
|
22
|
+
origin: string;
|
|
23
|
+
/** Absent for the web's workspace-less `/sessions/:id` links. */
|
|
24
|
+
workspaceId: string | null;
|
|
25
|
+
/** Absent for a link to the workspace itself. */
|
|
26
|
+
sessionId: string | null;
|
|
27
|
+
/** The session moment the link points at (`?at=`). */
|
|
28
|
+
at: number | null;
|
|
29
|
+
/** The web address the link came from, for the browser fallback. */
|
|
30
|
+
webUrl: string;
|
|
31
|
+
}
|
|
32
|
+
/** The web link a raw URL points at, unwrapping the web's "Open in app" handoff. */
|
|
33
|
+
export declare function parseAppLink(raw: string): AppLink | null;
|
|
34
|
+
/**
|
|
35
|
+
* The signed-in account a link belongs to, or null when the app has none on
|
|
36
|
+
* that deployment (the browser opens it instead). With several accounts on the
|
|
37
|
+
* same deployment: the one last used in the link's workspace, else the active
|
|
38
|
+
* one, else the first.
|
|
39
|
+
*/
|
|
40
|
+
export declare function accountForAppLink<Account extends AppLinkAccount>(link: AppLink, accounts: readonly Account[], activeId: string | null): Account | null;
|
|
41
|
+
/** The web's handoff link that opens a web page in the app. */
|
|
42
|
+
export declare function appHandoffUrl(webUrl: string): string;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// src/app-links.ts
|
|
2
|
+
function normalizeServerUrl(input) {
|
|
3
|
+
const raw = input.trim();
|
|
4
|
+
if (!raw) return null;
|
|
5
|
+
const withScheme = /^[a-z][a-z0-9+.-]*:\/\//iu.test(raw) ? raw : `https://${raw}`;
|
|
6
|
+
const match = /^(https?):\/\/([a-z0-9.-]+(?::\d{1,5})?)(?:[/?#].*)?$/iu.exec(withScheme);
|
|
7
|
+
return match ? `${match[1].toLowerCase()}://${match[2].toLowerCase()}` : null;
|
|
8
|
+
}
|
|
9
|
+
function serverLabel(baseUrl) {
|
|
10
|
+
return baseUrl.replace(/^https?:\/\//iu, "");
|
|
11
|
+
}
|
|
12
|
+
var HANDOFF = /^opengeni:\/\/open\/?\?(.*)$/iu;
|
|
13
|
+
var WEB_URL = /^(https?:\/\/[^/?#]+)(\/[^?#]*)?(\?[^#]*)?(#.*)?$/iu;
|
|
14
|
+
var ID = "([A-Za-z0-9][A-Za-z0-9_-]{0,127})";
|
|
15
|
+
var SESSION_PATH = new RegExp(`^/workspaces/${ID}/sessions/${ID}/?$`, "u");
|
|
16
|
+
var WORKSPACE_PATH = new RegExp(`^/workspaces/${ID}(?:/sessions)?/?$`, "u");
|
|
17
|
+
var BARE_SESSION_PATH = new RegExp(`^/sessions/${ID}/?$`, "u");
|
|
18
|
+
function queryParam(query, name) {
|
|
19
|
+
for (const part of (query ?? "").replace(/^\?/u, "").split("&")) {
|
|
20
|
+
const [key, value = ""] = part.split("=");
|
|
21
|
+
if (key !== name) continue;
|
|
22
|
+
try {
|
|
23
|
+
return decodeURIComponent(value.replace(/\+/gu, " "));
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
function parseAppLink(raw) {
|
|
31
|
+
const trimmed = raw.trim();
|
|
32
|
+
const handoff = HANDOFF.exec(trimmed);
|
|
33
|
+
const webUrl = handoff ? queryParam(handoff[1], "url") : trimmed;
|
|
34
|
+
if (!webUrl) return null;
|
|
35
|
+
const match = WEB_URL.exec(webUrl);
|
|
36
|
+
if (!match) return null;
|
|
37
|
+
const origin = normalizeServerUrl(match[1]);
|
|
38
|
+
if (!origin) return null;
|
|
39
|
+
const path = match[2] ?? "/";
|
|
40
|
+
const atRaw = queryParam(match[3], "at");
|
|
41
|
+
const at = atRaw && /^[1-9][0-9]{0,15}$/u.test(atRaw) ? Number(atRaw) : null;
|
|
42
|
+
const session = SESSION_PATH.exec(path);
|
|
43
|
+
if (session) return { origin, workspaceId: session[1], sessionId: session[2], at, webUrl };
|
|
44
|
+
const bare = BARE_SESSION_PATH.exec(path);
|
|
45
|
+
if (bare) return { origin, workspaceId: null, sessionId: bare[1], at, webUrl };
|
|
46
|
+
const workspace = WORKSPACE_PATH.exec(path);
|
|
47
|
+
if (workspace) return { origin, workspaceId: workspace[1], sessionId: null, at: null, webUrl };
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
function accountForAppLink(link, accounts, activeId) {
|
|
51
|
+
const candidates = accounts.filter(
|
|
52
|
+
(account) => !account.signedOut && normalizeServerUrl(account.baseUrl) === link.origin
|
|
53
|
+
);
|
|
54
|
+
return (link.workspaceId ? candidates.find((account) => account.workspaceId === link.workspaceId) : void 0) ?? candidates.find((account) => account.id === activeId) ?? candidates[0] ?? null;
|
|
55
|
+
}
|
|
56
|
+
function appHandoffUrl(webUrl) {
|
|
57
|
+
return `opengeni://open?url=${encodeURIComponent(webUrl)}`;
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
accountForAppLink,
|
|
61
|
+
appHandoffUrl,
|
|
62
|
+
normalizeServerUrl,
|
|
63
|
+
parseAppLink,
|
|
64
|
+
serverLabel
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=app-links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/app-links.ts"],"sourcesContent":["// Opengeni web links that a native app (phone or desktop) can show itself, and\n// the account each belongs to. Pure: no platform modules, so every app shares it.\n\n/** A deployment address as typed: trimmed, https by default, no trailing slash. */\nexport function normalizeServerUrl(input: string): string | null {\n const raw = input.trim();\n if (!raw) return null;\n const withScheme = /^[a-z][a-z0-9+.-]*:\\/\\//iu.test(raw) ? raw : `https://${raw}`;\n const match = /^(https?):\\/\\/([a-z0-9.-]+(?::\\d{1,5})?)(?:[/?#].*)?$/iu.exec(withScheme);\n return match ? `${match[1]!.toLowerCase()}://${match[2]!.toLowerCase()}` : null;\n}\n\n/** The host a person recognizes, e.g. app.opengeni.ai. */\nexport function serverLabel(baseUrl: string): string {\n return baseUrl.replace(/^https?:\\/\\//iu, \"\");\n}\n\n/** The part of a signed-in app account that link routing reads. */\nexport interface AppLinkAccount {\n id: string;\n /** The deployment's web origin, e.g. https://app.opengeni.ai. */\n baseUrl: string;\n /** The workspace last used with this account. */\n workspaceId: string | null;\n /** Set when the deployment rejected the credential. */\n signedOut?: boolean;\n}\n\n/**\n * A web link the app can show itself: a conversation or a workspace on some\n * deployment. Everything else on the web (settings, organization, artifacts)\n * stays in the browser.\n */\nexport interface AppLink {\n /** The deployment's origin, normalized like a stored account's baseUrl. */\n origin: string;\n /** Absent for the web's workspace-less `/sessions/:id` links. */\n workspaceId: string | null;\n /** Absent for a link to the workspace itself. */\n sessionId: string | null;\n /** The session moment the link points at (`?at=`). */\n at: number | null;\n /** The web address the link came from, for the browser fallback. */\n webUrl: string;\n}\n\n/** The app's own scheme for links handed over from the web: opengeni://open?url=… */\nconst HANDOFF = /^opengeni:\\/\\/open\\/?\\?(.*)$/iu;\nconst WEB_URL = /^(https?:\\/\\/[^/?#]+)(\\/[^?#]*)?(\\?[^#]*)?(#.*)?$/iu;\nconst ID = \"([A-Za-z0-9][A-Za-z0-9_-]{0,127})\";\nconst SESSION_PATH = new RegExp(`^/workspaces/${ID}/sessions/${ID}/?$`, \"u\");\nconst WORKSPACE_PATH = new RegExp(`^/workspaces/${ID}(?:/sessions)?/?$`, \"u\");\nconst BARE_SESSION_PATH = new RegExp(`^/sessions/${ID}/?$`, \"u\");\n\nfunction queryParam(query: string | undefined, name: string): string | null {\n for (const part of (query ?? \"\").replace(/^\\?/u, \"\").split(\"&\")) {\n const [key, value = \"\"] = part.split(\"=\");\n if (key !== name) continue;\n try {\n return decodeURIComponent(value.replace(/\\+/gu, \" \"));\n } catch {\n return null;\n }\n }\n return null;\n}\n\n/** The web link a raw URL points at, unwrapping the web's \"Open in app\" handoff. */\nexport function parseAppLink(raw: string): AppLink | null {\n const trimmed = raw.trim();\n const handoff = HANDOFF.exec(trimmed);\n const webUrl = handoff ? queryParam(handoff[1], \"url\") : trimmed;\n if (!webUrl) return null;\n const match = WEB_URL.exec(webUrl);\n if (!match) return null;\n const origin = normalizeServerUrl(match[1]!);\n if (!origin) return null;\n const path = match[2] ?? \"/\";\n const atRaw = queryParam(match[3], \"at\");\n const at = atRaw && /^[1-9][0-9]{0,15}$/u.test(atRaw) ? Number(atRaw) : null;\n const session = SESSION_PATH.exec(path);\n if (session) return { origin, workspaceId: session[1]!, sessionId: session[2]!, at, webUrl };\n const bare = BARE_SESSION_PATH.exec(path);\n if (bare) return { origin, workspaceId: null, sessionId: bare[1]!, at, webUrl };\n const workspace = WORKSPACE_PATH.exec(path);\n if (workspace) return { origin, workspaceId: workspace[1]!, sessionId: null, at: null, webUrl };\n return null;\n}\n\n/**\n * The signed-in account a link belongs to, or null when the app has none on\n * that deployment (the browser opens it instead). With several accounts on the\n * same deployment: the one last used in the link's workspace, else the active\n * one, else the first.\n */\nexport function accountForAppLink<Account extends AppLinkAccount>(\n link: AppLink,\n accounts: readonly Account[],\n activeId: string | null,\n): Account | null {\n const candidates = accounts.filter(\n (account) => !account.signedOut && normalizeServerUrl(account.baseUrl) === link.origin,\n );\n return (\n (link.workspaceId\n ? candidates.find((account) => account.workspaceId === link.workspaceId)\n : undefined) ??\n candidates.find((account) => account.id === activeId) ??\n candidates[0] ??\n null\n );\n}\n\n/** The web's handoff link that opens a web page in the app. */\nexport function appHandoffUrl(webUrl: string): string {\n return `opengeni://open?url=${encodeURIComponent(webUrl)}`;\n}\n"],"mappings":";AAIO,SAAS,mBAAmB,OAA8B;AAC/D,QAAM,MAAM,MAAM,KAAK;AACvB,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,aAAa,4BAA4B,KAAK,GAAG,IAAI,MAAM,WAAW,GAAG;AAC/E,QAAM,QAAQ,0DAA0D,KAAK,UAAU;AACvF,SAAO,QAAQ,GAAG,MAAM,CAAC,EAAG,YAAY,CAAC,MAAM,MAAM,CAAC,EAAG,YAAY,CAAC,KAAK;AAC7E;AAGO,SAAS,YAAY,SAAyB;AACnD,SAAO,QAAQ,QAAQ,kBAAkB,EAAE;AAC7C;AAgCA,IAAM,UAAU;AAChB,IAAM,UAAU;AAChB,IAAM,KAAK;AACX,IAAM,eAAe,IAAI,OAAO,gBAAgB,EAAE,aAAa,EAAE,OAAO,GAAG;AAC3E,IAAM,iBAAiB,IAAI,OAAO,gBAAgB,EAAE,qBAAqB,GAAG;AAC5E,IAAM,oBAAoB,IAAI,OAAO,cAAc,EAAE,OAAO,GAAG;AAE/D,SAAS,WAAW,OAA2B,MAA6B;AAC1E,aAAW,SAAS,SAAS,IAAI,QAAQ,QAAQ,EAAE,EAAE,MAAM,GAAG,GAAG;AAC/D,UAAM,CAAC,KAAK,QAAQ,EAAE,IAAI,KAAK,MAAM,GAAG;AACxC,QAAI,QAAQ,KAAM;AAClB,QAAI;AACF,aAAO,mBAAmB,MAAM,QAAQ,QAAQ,GAAG,CAAC;AAAA,IACtD,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,aAAa,KAA6B;AACxD,QAAM,UAAU,IAAI,KAAK;AACzB,QAAM,UAAU,QAAQ,KAAK,OAAO;AACpC,QAAM,SAAS,UAAU,WAAW,QAAQ,CAAC,GAAG,KAAK,IAAI;AACzD,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,QAAQ,QAAQ,KAAK,MAAM;AACjC,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,SAAS,mBAAmB,MAAM,CAAC,CAAE;AAC3C,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,OAAO,MAAM,CAAC,KAAK;AACzB,QAAM,QAAQ,WAAW,MAAM,CAAC,GAAG,IAAI;AACvC,QAAM,KAAK,SAAS,sBAAsB,KAAK,KAAK,IAAI,OAAO,KAAK,IAAI;AACxE,QAAM,UAAU,aAAa,KAAK,IAAI;AACtC,MAAI,QAAS,QAAO,EAAE,QAAQ,aAAa,QAAQ,CAAC,GAAI,WAAW,QAAQ,CAAC,GAAI,IAAI,OAAO;AAC3F,QAAM,OAAO,kBAAkB,KAAK,IAAI;AACxC,MAAI,KAAM,QAAO,EAAE,QAAQ,aAAa,MAAM,WAAW,KAAK,CAAC,GAAI,IAAI,OAAO;AAC9E,QAAM,YAAY,eAAe,KAAK,IAAI;AAC1C,MAAI,UAAW,QAAO,EAAE,QAAQ,aAAa,UAAU,CAAC,GAAI,WAAW,MAAM,IAAI,MAAM,OAAO;AAC9F,SAAO;AACT;AAQO,SAAS,kBACd,MACA,UACA,UACgB;AAChB,QAAM,aAAa,SAAS;AAAA,IAC1B,CAAC,YAAY,CAAC,QAAQ,aAAa,mBAAmB,QAAQ,OAAO,MAAM,KAAK;AAAA,EAClF;AACA,UACG,KAAK,cACF,WAAW,KAAK,CAAC,YAAY,QAAQ,gBAAgB,KAAK,WAAW,IACrE,WACJ,WAAW,KAAK,CAAC,YAAY,QAAQ,OAAO,QAAQ,KACpD,WAAW,CAAC,KACZ;AAEJ;AAGO,SAAS,cAAc,QAAwB;AACpD,SAAO,uBAAuB,mBAAmB,MAAM,CAAC;AAC1D;","names":[]}
|