@agent-native/core 0.201.1 → 0.202.0-nightly-20261006033736
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/automation/index.d.ts +1 -1
- package/dist/client/analytics/index.d.ts +1 -1
- package/dist/client/analytics/index.js +1 -1
- package/dist/client/analytics.d.ts +23 -3
- package/dist/client/analytics.js +189 -46
- package/dist/client/client-status-requests.d.ts +4 -1
- package/dist/client/client-status-requests.js +21 -5
- package/dist/client/embed-auth.d.ts +6 -0
- package/dist/client/embed-auth.js +13 -0
- package/dist/client/session-replay.js +48 -22
- package/dist/client/sharing/index.d.ts +2 -0
- package/dist/client/sharing/index.js +2 -0
- package/dist/client/sharing/useAccessRequestReview.d.ts +80 -0
- package/dist/client/sharing/useAccessRequestReview.js +100 -0
- package/dist/client/sharing/useResourceAccessGate.d.ts +57 -0
- package/dist/client/sharing/useResourceAccessGate.js +76 -0
- package/dist/client/use-agent-engine-configured.d.ts +2 -0
- package/dist/client/use-agent-engine-configured.js +4 -1
- package/dist/email-catalog/registry.d.ts +1 -1
- package/dist/email-catalog/registry.js +1 -1
- package/dist/email-catalog/system-emails.d.ts +2 -2
- package/dist/email-catalog/system-emails.js +47 -2
- package/dist/email-catalog/templates.d.ts +5 -1
- package/dist/email-catalog/templates.js +11 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/framework-tools.js +6 -0
- package/dist/identity/rekey.js +9 -0
- package/dist/jobs/run-history.d.ts +27 -8
- package/dist/jobs/run-history.js +17 -10
- package/dist/jobs/scheduler.js +39 -10
- package/dist/mcp/build-server.d.ts +1 -0
- package/dist/mcp/build-server.js +30 -7
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +7 -7
- package/dist/org/index.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +10 -10
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/review/suggestions/actions.js +14 -35
- package/dist/review/suggestions/store.d.ts +0 -1
- package/dist/review/suggestions/store.js +0 -7
- package/dist/secrets/routes.d.ts +11 -11
- package/dist/server/action-discovery.js +24 -0
- package/dist/server/agent-access.js +7 -3
- package/dist/server/agent-chat/mcp-options.d.ts +1 -0
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-default-model-route.d.ts +1 -1
- package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
- package/dist/server/attribution.d.ts +33 -1
- package/dist/server/attribution.js +93 -21
- package/dist/server/auth.js +35 -11
- package/dist/server/email-templates.d.ts +34 -0
- package/dist/server/email-templates.js +40 -0
- package/dist/server/magic-link-attribution.js +2 -1
- package/dist/server/onboarding-html.js +35 -67
- package/dist/server/release-schema.js +4 -0
- package/dist/server/short-lived-token.d.ts +7 -0
- package/dist/server/short-lived-token.js +59 -0
- package/dist/shared/analytics-anonymous-id.d.ts +1 -0
- package/dist/shared/analytics-anonymous-id.js +1 -1
- package/dist/shared/attribution-source.d.ts +30 -0
- package/dist/shared/attribution-source.js +65 -0
- package/dist/sharing/access-request-store.d.ts +96 -0
- package/dist/sharing/access-request-store.js +213 -0
- package/dist/sharing/access-requests.d.ts +113 -0
- package/dist/sharing/access-requests.js +490 -0
- package/dist/sharing/access.d.ts +36 -2
- package/dist/sharing/access.js +74 -12
- package/dist/sharing/actions/approve-resource-access-request.d.ts +10 -0
- package/dist/sharing/actions/approve-resource-access-request.js +15 -0
- package/dist/sharing/actions/decline-resource-access-request.d.ts +7 -0
- package/dist/sharing/actions/decline-resource-access-request.js +14 -0
- package/dist/sharing/actions/get-resource-access-request.d.ts +4 -0
- package/dist/sharing/actions/get-resource-access-request.js +13 -0
- package/dist/sharing/actions/get-resource-access-status.d.ts +5 -0
- package/dist/sharing/actions/get-resource-access-status.js +19 -0
- package/dist/sharing/actions/list-resource-access-requests.d.ts +5 -0
- package/dist/sharing/actions/list-resource-access-requests.js +16 -0
- package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
- package/dist/sharing/actions/request-resource-access.d.ts +6 -0
- package/dist/sharing/actions/request-resource-access.js +17 -0
- package/dist/sharing/actions/share-resource.d.ts +1 -1
- package/dist/sharing/actions/share-resource.js +18 -144
- package/dist/sharing/grant.d.ts +49 -0
- package/dist/sharing/grant.js +195 -0
- package/dist/sharing/index.d.ts +2 -1
- package/dist/sharing/index.js +2 -1
- package/dist/sharing/registry.d.ts +36 -0
- package/dist/templates/chat/server/plugins/auth.ts +2 -2
- package/dist/templates/default/app/routes/access-requests.$requestId.tsx +7 -0
- package/dist/templates/default/server/plugins/auth.ts +2 -2
- package/dist/vite/action-types-plugin.js +24 -0
- package/package.json +11 -3
- package/src/templates/chat/server/plugins/auth.ts +2 -2
- package/src/templates/default/app/routes/access-requests.$requestId.tsx +7 -0
- package/src/templates/default/server/plugins/auth.ts +2 -2
|
@@ -139,8 +139,8 @@ export declare function createAutomationCallbackHandler(runtime: AutomationRunti
|
|
|
139
139
|
duplicate?: undefined;
|
|
140
140
|
eventId?: undefined;
|
|
141
141
|
} | {
|
|
142
|
+
error?: undefined;
|
|
142
143
|
accepted: boolean;
|
|
143
144
|
duplicate: boolean;
|
|
144
145
|
eventId: string;
|
|
145
|
-
error?: undefined;
|
|
146
146
|
}>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureAttribution, captureClientException, captureError, captureException, captureMessage, clearAnalyticsSessionId, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setAnalyticsSessionId, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackAnonymousEvent, trackEvent, trackLifecycleEvent, trackSessionStatus, type AgentChatLifecycleEvent, type CaptureExceptionContext, type CapturedExceptionEvent, type ClientCaptureContext, type ConfigureTrackingOptions, type ErrorCaptureConfigOptions, type ExceptionBreadcrumb, type ExceptionLevel, type FirstTouchAttribution, type SessionReplayContext, type SessionReplayLinkOptions, type SessionReplayOptions, type SessionReplayStartResult, type SessionReplayUrlMatcher, type TrackingIdentityUser, } from "../analytics.js";
|
|
1
|
+
export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureAttribution, captureClientException, captureError, captureException, captureMessage, clearAnalyticsSessionId, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getLastTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setAnalyticsSessionId, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackAnonymousEvent, trackEvent, trackLifecycleEvent, trackSessionStatus, type AgentChatLifecycleEvent, type CaptureExceptionContext, type CapturedExceptionEvent, type ClientCaptureContext, type ConfigureTrackingOptions, type ErrorCaptureConfigOptions, type ExceptionBreadcrumb, type ExceptionLevel, type FirstTouchAttribution, type LastTouchAttribution, type SessionReplayContext, type SessionReplayLinkOptions, type SessionReplayOptions, type SessionReplayStartResult, type SessionReplayUrlMatcher, type TrackingIdentityUser, } from "../analytics.js";
|
|
2
2
|
export { track } from "../track.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureAttribution, captureClientException, captureError, captureException, captureMessage, clearAnalyticsSessionId, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setAnalyticsSessionId, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackAnonymousEvent, trackEvent, trackLifecycleEvent, trackSessionStatus, } from "../analytics.js";
|
|
1
|
+
export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureAttribution, captureClientException, captureError, captureException, captureMessage, clearAnalyticsSessionId, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getLastTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setAnalyticsSessionId, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackAnonymousEvent, trackEvent, trackLifecycleEvent, trackSessionStatus, } from "../analytics.js";
|
|
2
2
|
export { track } from "../track.js";
|
|
@@ -86,14 +86,34 @@ export interface FirstTouchAttribution {
|
|
|
86
86
|
landed_at?: string;
|
|
87
87
|
capture_truncated?: string;
|
|
88
88
|
}
|
|
89
|
+
/** The latest visit that had a source; see `captureLastTouchAttribution`. */
|
|
90
|
+
export interface LastTouchAttribution {
|
|
91
|
+
ref?: string;
|
|
92
|
+
via?: string;
|
|
93
|
+
utm_source?: string;
|
|
94
|
+
utm_medium?: string;
|
|
95
|
+
utm_campaign?: string;
|
|
96
|
+
utm_content?: string;
|
|
97
|
+
utm_term?: string;
|
|
98
|
+
gclid?: string;
|
|
99
|
+
msclkid?: string;
|
|
100
|
+
vector_source?: string;
|
|
101
|
+
landing_referrer?: string;
|
|
102
|
+
site_referrer?: string;
|
|
103
|
+
site_landing_path?: string;
|
|
104
|
+
landing_path?: string;
|
|
105
|
+
touched_at?: string;
|
|
106
|
+
capture_truncated?: string;
|
|
107
|
+
}
|
|
89
108
|
export declare function getAnalyticsIdentityKey(): string | undefined;
|
|
90
109
|
export declare function getAnalyticsAnonymousId(): string | undefined;
|
|
91
110
|
export declare function getAnalyticsSessionId(): string | undefined;
|
|
92
111
|
export declare function getFirstTouchAttribution(): FirstTouchAttribution | null;
|
|
112
|
+
export declare function getLastTouchAttribution(): LastTouchAttribution | null;
|
|
93
113
|
/**
|
|
94
|
-
* Store the visitor's first touch now instead of in
|
|
95
|
-
* for a page that reads
|
|
96
|
-
* load, so tracking's own capture is then a no-op.
|
|
114
|
+
* Store the visitor's first and last touch now instead of in
|
|
115
|
+
* `configureTracking()`, for a page that reads them before tracking starts.
|
|
116
|
+
* It runs once per page load, so tracking's own capture is then a no-op.
|
|
97
117
|
*/
|
|
98
118
|
export declare function captureAttribution(): void;
|
|
99
119
|
export declare function setSentryUser(user: TrackingIdentityUser | null, orgId?: string | null): void;
|
package/dist/client/analytics.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { recordTrackingEvent } from "../observability/tracing.js";
|
|
2
2
|
import { AGENT_NATIVE_LIFECYCLE_EVENTS, canonicalTrackingEvent, legacyLifecycleEvent, normalizeTrackingDimension, withCanonicalTrackingProperties, } from "../shared/analytics-events.js";
|
|
3
3
|
import { ANALYTICS_CLIENT_PLATFORM_PROPERTY, } from "../shared/analytics-platform.js";
|
|
4
|
+
import { hasAttributionSource } from "../shared/attribution-source.js";
|
|
4
5
|
import { resolveLaneEndpoint } from "../shared/environment-lanes.js";
|
|
5
6
|
import { classifyErrorNoise, } from "../shared/error-noise.js";
|
|
6
7
|
import { llmConnectionTrackingProperties, } from "../shared/llm-connection.js";
|
|
@@ -101,6 +102,72 @@ const FIRST_TOUCH_COOKIE_FIELD_PRIORITY = [
|
|
|
101
102
|
"site_landing_path",
|
|
102
103
|
"landed_at",
|
|
103
104
|
];
|
|
105
|
+
const LAST_TOUCH_STORAGE_KEY = "an_last_touch";
|
|
106
|
+
const LAST_TOUCH_COOKIE_NAME = "an_lt";
|
|
107
|
+
// Small enough that both cookies still fit the signup handoff header.
|
|
108
|
+
const LAST_TOUCH_MAX_COOKIE_BYTES = 700;
|
|
109
|
+
// What a sourced visit keeps as last touch, besides its path and time.
|
|
110
|
+
const LAST_TOUCH_SOURCE_FIELDS = [
|
|
111
|
+
"ref",
|
|
112
|
+
"via",
|
|
113
|
+
"utm_source",
|
|
114
|
+
"utm_medium",
|
|
115
|
+
"utm_campaign",
|
|
116
|
+
"utm_content",
|
|
117
|
+
"utm_term",
|
|
118
|
+
"gclid",
|
|
119
|
+
"msclkid",
|
|
120
|
+
"vector_source",
|
|
121
|
+
"landing_referrer",
|
|
122
|
+
"site_referrer",
|
|
123
|
+
];
|
|
124
|
+
// Which fields the cookie keeps first when they don't all fit.
|
|
125
|
+
const LAST_TOUCH_COOKIE_FIELD_PRIORITY = [
|
|
126
|
+
"ref",
|
|
127
|
+
"gclid",
|
|
128
|
+
"msclkid",
|
|
129
|
+
"vector_source",
|
|
130
|
+
"utm_source",
|
|
131
|
+
"utm_medium",
|
|
132
|
+
"utm_campaign",
|
|
133
|
+
"via",
|
|
134
|
+
"utm_content",
|
|
135
|
+
"utm_term",
|
|
136
|
+
"landing_referrer",
|
|
137
|
+
"site_referrer",
|
|
138
|
+
"landing_path",
|
|
139
|
+
"touched_at",
|
|
140
|
+
"site_landing_path",
|
|
141
|
+
];
|
|
142
|
+
// The marketing site forwards its own last touch under these names when it
|
|
143
|
+
// differs from the first touch it forwards as plain campaign params.
|
|
144
|
+
const FORWARDED_LAST_TOUCH_FIELDS = {
|
|
145
|
+
last_ref: "ref",
|
|
146
|
+
last_via: "via",
|
|
147
|
+
last_utm_source: "utm_source",
|
|
148
|
+
last_utm_medium: "utm_medium",
|
|
149
|
+
last_utm_campaign: "utm_campaign",
|
|
150
|
+
last_utm_content: "utm_content",
|
|
151
|
+
last_utm_term: "utm_term",
|
|
152
|
+
last_gclid: "gclid",
|
|
153
|
+
last_msclkid: "msclkid",
|
|
154
|
+
last_vector_source: "vector_source",
|
|
155
|
+
last_referrer: "site_referrer",
|
|
156
|
+
last_landing_path: "site_landing_path",
|
|
157
|
+
};
|
|
158
|
+
// When the site's latest sourced visit happened, so an older site visit can't
|
|
159
|
+
// replace a newer one the app already has.
|
|
160
|
+
const FORWARDED_LAST_TOUCH_AT_PARAM = "last_at";
|
|
161
|
+
const FIRST_TOUCH_COOKIE = {
|
|
162
|
+
name: FIRST_TOUCH_COOKIE_NAME,
|
|
163
|
+
maxBytes: FIRST_TOUCH_MAX_COOKIE_BYTES,
|
|
164
|
+
priority: FIRST_TOUCH_COOKIE_FIELD_PRIORITY,
|
|
165
|
+
};
|
|
166
|
+
const LAST_TOUCH_COOKIE = {
|
|
167
|
+
name: LAST_TOUCH_COOKIE_NAME,
|
|
168
|
+
maxBytes: LAST_TOUCH_MAX_COOKIE_BYTES,
|
|
169
|
+
priority: LAST_TOUCH_COOKIE_FIELD_PRIORITY,
|
|
170
|
+
};
|
|
104
171
|
let _firstTouchCaptured = false;
|
|
105
172
|
function safeStorageGet(key) {
|
|
106
173
|
try {
|
|
@@ -390,7 +457,7 @@ function buildFirstTouchAttribution() {
|
|
|
390
457
|
attribution.landed_at = new Date().toISOString();
|
|
391
458
|
return attribution;
|
|
392
459
|
}
|
|
393
|
-
function
|
|
460
|
+
function readAttributionCookie(cookieName) {
|
|
394
461
|
if (typeof document === "undefined")
|
|
395
462
|
return null;
|
|
396
463
|
try {
|
|
@@ -400,7 +467,7 @@ function readFirstTouchCookie() {
|
|
|
400
467
|
if (eq === -1)
|
|
401
468
|
continue;
|
|
402
469
|
const name = part.slice(0, eq).trim();
|
|
403
|
-
if (name ===
|
|
470
|
+
if (name === cookieName) {
|
|
404
471
|
return part.slice(eq + 1).trim();
|
|
405
472
|
}
|
|
406
473
|
}
|
|
@@ -410,15 +477,16 @@ function readFirstTouchCookie() {
|
|
|
410
477
|
}
|
|
411
478
|
return null;
|
|
412
479
|
}
|
|
413
|
-
function
|
|
414
|
-
return (`${
|
|
480
|
+
function attributionCookieAssignment(cookieName, encodedValue) {
|
|
481
|
+
return (`${cookieName}=${encodedValue}; path=/; ` +
|
|
415
482
|
`max-age=${FIRST_TOUCH_COOKIE_MAX_AGE_SECONDS}; SameSite=Lax`);
|
|
416
483
|
}
|
|
417
|
-
function
|
|
484
|
+
function fitAttributionCookieValue(value, spec) {
|
|
418
485
|
const source = JSON.parse(value);
|
|
419
486
|
const compact = {};
|
|
420
487
|
let truncated = false;
|
|
421
|
-
|
|
488
|
+
const fits = (encoded) => attributionCookieAssignment(spec.name, encoded).length <= spec.maxBytes;
|
|
489
|
+
for (const field of spec.priority) {
|
|
422
490
|
const rawValue = source[field];
|
|
423
491
|
if (typeof rawValue !== "string" || !rawValue)
|
|
424
492
|
continue;
|
|
@@ -426,9 +494,8 @@ function fitFirstTouchCookieValue(value) {
|
|
|
426
494
|
...compact,
|
|
427
495
|
[field]: rawValue.slice(0, FIRST_TOUCH_MAX_FIELD_LENGTH),
|
|
428
496
|
};
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
Object.assign(compact, { [field]: candidate[field] });
|
|
497
|
+
if (fits(encodeURIComponent(JSON.stringify(candidate)))) {
|
|
498
|
+
compact[field] = candidate[field];
|
|
432
499
|
}
|
|
433
500
|
else {
|
|
434
501
|
truncated = true;
|
|
@@ -437,38 +504,63 @@ function fitFirstTouchCookieValue(value) {
|
|
|
437
504
|
if (truncated) {
|
|
438
505
|
compact.capture_truncated = "1";
|
|
439
506
|
// Keep the auth handoff under its 4 KB header limit after re-encoding.
|
|
440
|
-
for (const field of [...
|
|
507
|
+
for (const field of [...spec.priority].reverse()) {
|
|
441
508
|
const encoded = encodeURIComponent(JSON.stringify(compact));
|
|
442
|
-
if (
|
|
443
|
-
FIRST_TOUCH_MAX_COOKIE_BYTES) {
|
|
509
|
+
if (fits(encoded))
|
|
444
510
|
return encoded;
|
|
445
|
-
}
|
|
446
511
|
delete compact[field];
|
|
447
512
|
}
|
|
448
513
|
}
|
|
449
514
|
const encoded = encodeURIComponent(JSON.stringify(compact));
|
|
450
|
-
if (
|
|
451
|
-
throw new Error(
|
|
515
|
+
if (!fits(encoded)) {
|
|
516
|
+
throw new Error(`Attribution exceeded the ${spec.name} cookie budget`);
|
|
452
517
|
}
|
|
453
518
|
return encoded;
|
|
454
519
|
}
|
|
455
|
-
function
|
|
520
|
+
function writeAttributionCookie(value, spec) {
|
|
456
521
|
if (typeof document === "undefined")
|
|
457
522
|
return;
|
|
458
|
-
const encodedValue =
|
|
523
|
+
const encodedValue = fitAttributionCookieValue(value, spec);
|
|
459
524
|
try {
|
|
460
|
-
document.cookie =
|
|
525
|
+
document.cookie = attributionCookieAssignment(spec.name, encodedValue);
|
|
461
526
|
}
|
|
462
527
|
catch {
|
|
463
528
|
// best-effort
|
|
464
529
|
}
|
|
465
530
|
}
|
|
531
|
+
function storeAttribution(storageKey, spec, attribution) {
|
|
532
|
+
const json = JSON.stringify(attribution);
|
|
533
|
+
safeStorageSet(storageKey, json);
|
|
534
|
+
writeAttributionCookie(json, spec);
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Restore a cookie that expired or was cleared from its stored value, so the
|
|
538
|
+
* signup boundary still sees it.
|
|
539
|
+
*/
|
|
540
|
+
function backfillAttributionCookie(storageKey, spec) {
|
|
541
|
+
if (readAttributionCookie(spec.name))
|
|
542
|
+
return;
|
|
543
|
+
const stored = safeStorageGet(storageKey);
|
|
544
|
+
if (!stored)
|
|
545
|
+
return;
|
|
546
|
+
try {
|
|
547
|
+
writeAttributionCookie(stored, spec);
|
|
548
|
+
}
|
|
549
|
+
catch {
|
|
550
|
+
// coercion-ok: localStorage still holds the value; only this page's
|
|
551
|
+
// signup handoff goes without it.
|
|
552
|
+
}
|
|
553
|
+
}
|
|
466
554
|
/**
|
|
467
|
-
* Capture the visitor's
|
|
468
|
-
*
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
*
|
|
555
|
+
* Capture the visitor's referral attribution once per page load, into both
|
|
556
|
+
* `localStorage` and a first-party cookie the signup boundary reads. Fully
|
|
557
|
+
* defensive and SSR-safe — any failure is swallowed so it can never break app
|
|
558
|
+
* boot.
|
|
559
|
+
*
|
|
560
|
+
* First touch (`an_attribution` / `an_ft`) is first-write-wins, except over a
|
|
561
|
+
* visit that had no source: the first visit that says where this person came
|
|
562
|
+
* from replaces it. Without that, an untagged first visit would hide every
|
|
563
|
+
* tagged visit after it.
|
|
472
564
|
*/
|
|
473
565
|
function captureFirstTouchAttribution() {
|
|
474
566
|
if (_firstTouchCaptured)
|
|
@@ -477,35 +569,80 @@ function captureFirstTouchAttribution() {
|
|
|
477
569
|
if (typeof window === "undefined")
|
|
478
570
|
return;
|
|
479
571
|
try {
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
}
|
|
489
|
-
catch {
|
|
490
|
-
// ignore
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
return;
|
|
572
|
+
const current = buildFirstTouchAttribution();
|
|
573
|
+
const existing = getFirstTouchAttribution();
|
|
574
|
+
if (existing &&
|
|
575
|
+
(hasAttributionSource(existing) || !hasAttributionSource(current))) {
|
|
576
|
+
backfillAttributionCookie(FIRST_TOUCH_STORAGE_KEY, FIRST_TOUCH_COOKIE);
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
storeAttribution(FIRST_TOUCH_STORAGE_KEY, FIRST_TOUCH_COOKIE, current);
|
|
494
580
|
}
|
|
495
|
-
|
|
496
|
-
const json = JSON.stringify(attribution);
|
|
497
|
-
safeStorageSet(FIRST_TOUCH_STORAGE_KEY, json);
|
|
498
|
-
writeFirstTouchCookie(json);
|
|
581
|
+
captureLastTouchAttribution(current);
|
|
499
582
|
}
|
|
500
583
|
catch {
|
|
501
584
|
// Attribution is best-effort telemetry; never let it break boot.
|
|
502
585
|
}
|
|
503
586
|
}
|
|
504
|
-
|
|
587
|
+
/**
|
|
588
|
+
* Last touch (`an_last_touch` / `an_lt`) is the latest visit that had a
|
|
589
|
+
* source. When the marketing site forwards its own last touch, that visit is
|
|
590
|
+
* the one to keep, not the site's first touch riding on the same link. A site
|
|
591
|
+
* visit keeps the time it happened, and loses to a newer visit the app has
|
|
592
|
+
* already recorded.
|
|
593
|
+
*/
|
|
594
|
+
function captureLastTouchAttribution(current) {
|
|
595
|
+
const forwarded = readForwardedLastTouch();
|
|
596
|
+
const source = forwarded ?? (hasAttributionSource(current) ? current : null);
|
|
597
|
+
const forwardedAt = readForwardedLastTouchAt();
|
|
598
|
+
const existing = getLastTouchAttribution();
|
|
599
|
+
const existingAt = Date.parse(existing?.touched_at ?? "");
|
|
600
|
+
if (!source ||
|
|
601
|
+
(forwardedAt &&
|
|
602
|
+
hasAttributionSource(existing) &&
|
|
603
|
+
existingAt > Date.parse(forwardedAt))) {
|
|
604
|
+
backfillAttributionCookie(LAST_TOUCH_STORAGE_KEY, LAST_TOUCH_COOKIE);
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
const lastTouch = {};
|
|
608
|
+
for (const field of LAST_TOUCH_SOURCE_FIELDS) {
|
|
609
|
+
const value = source[field];
|
|
610
|
+
if (value)
|
|
611
|
+
lastTouch[field] = value;
|
|
612
|
+
}
|
|
613
|
+
// As for first touch, `landing_path` is where the visitor entered this app
|
|
614
|
+
// and `site_landing_path` the marketing-site page the touch landed on.
|
|
615
|
+
if (current.landing_path)
|
|
616
|
+
lastTouch.landing_path = current.landing_path;
|
|
617
|
+
if (source.site_landing_path) {
|
|
618
|
+
lastTouch.site_landing_path = source.site_landing_path;
|
|
619
|
+
}
|
|
620
|
+
lastTouch.touched_at = forwardedAt ?? current.landed_at;
|
|
621
|
+
storeAttribution(LAST_TOUCH_STORAGE_KEY, LAST_TOUCH_COOKIE, lastTouch);
|
|
622
|
+
}
|
|
623
|
+
function readForwardedLastTouchAt() {
|
|
624
|
+
const raw = new URLSearchParams(window.location.search).get(FORWARDED_LAST_TOUCH_AT_PARAM);
|
|
625
|
+
const time = Date.parse(raw ?? "");
|
|
626
|
+
// A visit can't be in the future; a bad clock or value means "now".
|
|
627
|
+
if (!Number.isFinite(time) || time > Date.now())
|
|
628
|
+
return undefined;
|
|
629
|
+
return new Date(time).toISOString();
|
|
630
|
+
}
|
|
631
|
+
function readForwardedLastTouch() {
|
|
632
|
+
const params = new URLSearchParams(window.location.search);
|
|
633
|
+
const forwarded = {};
|
|
634
|
+
for (const [param, field] of Object.entries(FORWARDED_LAST_TOUCH_FIELDS)) {
|
|
635
|
+
const value = truncateFirstTouchField(params.get(param));
|
|
636
|
+
if (value)
|
|
637
|
+
forwarded[field] = value;
|
|
638
|
+
}
|
|
639
|
+
return hasAttributionSource(forwarded) ? forwarded : null;
|
|
640
|
+
}
|
|
641
|
+
function readStoredAttribution(storageKey) {
|
|
505
642
|
if (typeof window === "undefined")
|
|
506
643
|
return null;
|
|
507
644
|
try {
|
|
508
|
-
const raw = safeStorageGet(
|
|
645
|
+
const raw = safeStorageGet(storageKey);
|
|
509
646
|
if (!raw)
|
|
510
647
|
return null;
|
|
511
648
|
const parsed = JSON.parse(raw);
|
|
@@ -518,10 +655,16 @@ export function getFirstTouchAttribution() {
|
|
|
518
655
|
return null;
|
|
519
656
|
}
|
|
520
657
|
}
|
|
658
|
+
export function getFirstTouchAttribution() {
|
|
659
|
+
return readStoredAttribution(FIRST_TOUCH_STORAGE_KEY);
|
|
660
|
+
}
|
|
661
|
+
export function getLastTouchAttribution() {
|
|
662
|
+
return readStoredAttribution(LAST_TOUCH_STORAGE_KEY);
|
|
663
|
+
}
|
|
521
664
|
/**
|
|
522
|
-
* Store the visitor's first touch now instead of in
|
|
523
|
-
* for a page that reads
|
|
524
|
-
* load, so tracking's own capture is then a no-op.
|
|
665
|
+
* Store the visitor's first and last touch now instead of in
|
|
666
|
+
* `configureTracking()`, for a page that reads them before tracking starts.
|
|
667
|
+
* It runs once per page load, so tracking's own capture is then a no-op.
|
|
525
668
|
*/
|
|
526
669
|
export function captureAttribution() {
|
|
527
670
|
if (isSyntheticBrowserTraffic())
|
|
@@ -4,6 +4,7 @@ export type ClientStatusResult<T> = {
|
|
|
4
4
|
} | {
|
|
5
5
|
state: "unavailable";
|
|
6
6
|
status?: number;
|
|
7
|
+
stale?: boolean;
|
|
7
8
|
};
|
|
8
9
|
declare global {
|
|
9
10
|
interface Window {
|
|
@@ -26,7 +27,9 @@ export declare function invalidateClientStatusRequest(path: string): void;
|
|
|
26
27
|
*/
|
|
27
28
|
export declare function expireClientStatusResult(path: string): void;
|
|
28
29
|
export declare function invalidateClientStatusRequests(): void;
|
|
29
|
-
export declare function fetchAgentEngineStatus<T = unknown>(
|
|
30
|
+
export declare function fetchAgentEngineStatus<T = unknown>(options?: {
|
|
31
|
+
fresh?: boolean;
|
|
32
|
+
}): Promise<ClientStatusResult<T>>;
|
|
30
33
|
export declare function fetchEnvironmentStatus<T = unknown>(): Promise<ClientStatusResult<T>>;
|
|
31
34
|
export declare function fetchBuilderStatus<T = unknown>(): Promise<ClientStatusResult<T>>;
|
|
32
35
|
export declare function fetchFileUploadStatus<T = unknown>(): Promise<ClientStatusResult<T>>;
|
|
@@ -14,6 +14,8 @@ const REQUEST_TIMEOUT_MS = 15_000;
|
|
|
14
14
|
const SESSION_STATUS_PATH = "/_agent-native/auth/session";
|
|
15
15
|
const cache = new Map();
|
|
16
16
|
const requests = new Map();
|
|
17
|
+
const supersededRequests = new WeakSet();
|
|
18
|
+
const freshRequests = new WeakSet();
|
|
17
19
|
const requestControllers = new Map();
|
|
18
20
|
const requestGenerations = new Map();
|
|
19
21
|
let invalidationListenersInstalled = false;
|
|
@@ -54,19 +56,28 @@ function installInvalidationListeners() {
|
|
|
54
56
|
});
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
|
-
async function fetchClientStatus(path) {
|
|
59
|
+
async function fetchClientStatus(path, options) {
|
|
58
60
|
if (agentNativeApiDisabledReason())
|
|
59
61
|
return { state: "unavailable" };
|
|
60
62
|
installInvalidationListeners();
|
|
61
63
|
const url = agentNativePath(path);
|
|
62
64
|
const cached = cache.get(url);
|
|
63
|
-
if (cached && cached.expiresAt > Date.now()) {
|
|
65
|
+
if (!options?.fresh && cached && cached.expiresAt > Date.now()) {
|
|
64
66
|
return cached.result;
|
|
65
67
|
}
|
|
66
68
|
cache.delete(url);
|
|
67
69
|
const pending = requests.get(url);
|
|
68
|
-
if (pending)
|
|
70
|
+
if (pending && options?.fresh && freshRequests.has(pending)) {
|
|
69
71
|
return pending;
|
|
72
|
+
}
|
|
73
|
+
if (pending && !options?.fresh) {
|
|
74
|
+
return pending;
|
|
75
|
+
}
|
|
76
|
+
if (options?.fresh) {
|
|
77
|
+
if (pending)
|
|
78
|
+
supersededRequests.add(pending);
|
|
79
|
+
requestGenerations.set(url, (requestGenerations.get(url) ?? 0) + 1);
|
|
80
|
+
}
|
|
70
81
|
const sessionRead = path === SESSION_STATUS_PATH;
|
|
71
82
|
const currentGeneration = () => sessionRead ? sessionGeneration : statusGeneration;
|
|
72
83
|
const requestGeneration = currentGeneration();
|
|
@@ -104,6 +115,9 @@ async function fetchClientStatus(path) {
|
|
|
104
115
|
.catch(() => ({ state: "unavailable" }));
|
|
105
116
|
const request = Promise.race([transport, timeout])
|
|
106
117
|
.then((result) => {
|
|
118
|
+
if (supersededRequests.has(request)) {
|
|
119
|
+
return fetchClientStatus(path);
|
|
120
|
+
}
|
|
107
121
|
if (currentGeneration() === requestGeneration &&
|
|
108
122
|
(requestGenerations.get(url) ?? 0) === requestUrlGeneration &&
|
|
109
123
|
result.state === "available") {
|
|
@@ -127,6 +141,8 @@ async function fetchClientStatus(path) {
|
|
|
127
141
|
}
|
|
128
142
|
});
|
|
129
143
|
requests.set(url, request);
|
|
144
|
+
if (options?.fresh)
|
|
145
|
+
freshRequests.add(request);
|
|
130
146
|
if (controller)
|
|
131
147
|
requestControllers.set(url, controller);
|
|
132
148
|
return request;
|
|
@@ -162,8 +178,8 @@ export function invalidateClientStatusRequests() {
|
|
|
162
178
|
requestControllers.clear();
|
|
163
179
|
requests.clear();
|
|
164
180
|
}
|
|
165
|
-
export function fetchAgentEngineStatus() {
|
|
166
|
-
return fetchClientStatus("/_agent-native/agent-engine/status");
|
|
181
|
+
export function fetchAgentEngineStatus(options) {
|
|
182
|
+
return fetchClientStatus("/_agent-native/agent-engine/status", options);
|
|
167
183
|
}
|
|
168
184
|
export function fetchEnvironmentStatus() {
|
|
169
185
|
return fetchClientStatus("/_agent-native/env-status");
|
|
@@ -4,4 +4,10 @@ export declare function isEmbedMcpChatBridgeActive(): boolean;
|
|
|
4
4
|
export declare function getEmbedAuthToken(): string | null;
|
|
5
5
|
export declare function isEmbedAuthActive(): boolean;
|
|
6
6
|
export declare function _resetEmbedAuthForTests(): void;
|
|
7
|
+
/**
|
|
8
|
+
* Forgets every replayed refusal. Call it when access is known to have
|
|
9
|
+
* changed, such as a link's status turning `allowed`: a refusal recorded
|
|
10
|
+
* before then would otherwise fail the next read for up to a minute.
|
|
11
|
+
*/
|
|
12
|
+
export declare function forgetAuthFailures(): void;
|
|
7
13
|
export declare function ensureEmbedAuthFetchInterceptor(): void;
|
|
@@ -349,6 +349,11 @@ function isAuthFailureStatus(status) {
|
|
|
349
349
|
return status === 401 || status === 403;
|
|
350
350
|
}
|
|
351
351
|
function shouldGuardAuthFailure(method, url) {
|
|
352
|
+
// Only an embed replays refusals, so a missing or expired embed token can't
|
|
353
|
+
// set off a retry storm. Elsewhere a refusal can lift mid-session, as when
|
|
354
|
+
// someone shares the page, and a replayed one would hide that for a minute.
|
|
355
|
+
if (!isEmbedAuthActive())
|
|
356
|
+
return false;
|
|
352
357
|
if (!GUARDED_METHODS.has(method))
|
|
353
358
|
return false;
|
|
354
359
|
if (url.pathname === EMBED_START_PATH)
|
|
@@ -415,6 +420,14 @@ async function recordAuthFailure(key, response) {
|
|
|
415
420
|
function clearAuthFailure(key) {
|
|
416
421
|
authFailureCache.delete(key);
|
|
417
422
|
}
|
|
423
|
+
/**
|
|
424
|
+
* Forgets every replayed refusal. Call it when access is known to have
|
|
425
|
+
* changed, such as a link's status turning `allowed`: a refusal recorded
|
|
426
|
+
* before then would otherwise fail the next read for up to a minute.
|
|
427
|
+
*/
|
|
428
|
+
export function forgetAuthFailures() {
|
|
429
|
+
authFailureCache.clear();
|
|
430
|
+
}
|
|
418
431
|
function withEmbedAuthHeaders(input, init, token, win) {
|
|
419
432
|
const method = requestMethod(input, init);
|
|
420
433
|
const url = inputUrl(input, win);
|
|
@@ -1114,8 +1114,9 @@ async function awaitReplayUpload(request, timeout) {
|
|
|
1114
1114
|
async function sendReplayUpload(options, body, callbacks = {}) {
|
|
1115
1115
|
if (isCrossOriginReplayEndpoint(options.endpoint)) {
|
|
1116
1116
|
const canUseKeepalive = canUseReplayKeepalive(body);
|
|
1117
|
-
if (canUseKeepalive)
|
|
1118
|
-
|
|
1117
|
+
if (callbacks.beforeUpload?.(canUseKeepalive) === "defer") {
|
|
1118
|
+
return "deferred";
|
|
1119
|
+
}
|
|
1119
1120
|
const timeout = startReplayUploadTimeout();
|
|
1120
1121
|
await awaitReplayUploadRequest(timeout, () => fetch(options.endpoint, {
|
|
1121
1122
|
method: "POST",
|
|
@@ -1124,12 +1125,12 @@ async function sendReplayUpload(options, body, callbacks = {}) {
|
|
|
1124
1125
|
headers: { "Content-Type": "text/plain;charset=UTF-8" },
|
|
1125
1126
|
signal: timeout.signal,
|
|
1126
1127
|
}));
|
|
1127
|
-
return;
|
|
1128
|
+
return "sent";
|
|
1128
1129
|
}
|
|
1129
1130
|
const upload = await buildReplayUploadBody(body);
|
|
1130
1131
|
const canUseKeepalive = canUseReplayKeepalive(upload.body);
|
|
1131
|
-
if (canUseKeepalive)
|
|
1132
|
-
|
|
1132
|
+
if (callbacks.beforeUpload?.(canUseKeepalive) === "defer")
|
|
1133
|
+
return "deferred";
|
|
1133
1134
|
const timeout = startReplayUploadTimeout();
|
|
1134
1135
|
await awaitReplayUploadRequest(timeout, () => fetch(options.endpoint, {
|
|
1135
1136
|
method: "POST",
|
|
@@ -1141,6 +1142,7 @@ async function sendReplayUpload(options, body, callbacks = {}) {
|
|
|
1141
1142
|
},
|
|
1142
1143
|
signal: timeout.signal,
|
|
1143
1144
|
}));
|
|
1145
|
+
return "sent";
|
|
1144
1146
|
}
|
|
1145
1147
|
function isFinalFlushReason(reason) {
|
|
1146
1148
|
return [
|
|
@@ -1157,6 +1159,16 @@ function isFinalFlushReason(reason) {
|
|
|
1157
1159
|
function isTerminalReplayFlushReason(reason) {
|
|
1158
1160
|
return reason === "pagehide" || reason === "beforeunload";
|
|
1159
1161
|
}
|
|
1162
|
+
/**
|
|
1163
|
+
* A navigation fires `visibilitychange` before or after `pagehide`, depending
|
|
1164
|
+
* on the browser, and a mobile page may get only `visibilitychange`. A page in
|
|
1165
|
+
* the back/forward cache can be evicted without running again.
|
|
1166
|
+
*/
|
|
1167
|
+
function isPageLeaveReplayFlushReason(reason) {
|
|
1168
|
+
return (isTerminalReplayFlushReason(reason) ||
|
|
1169
|
+
reason === "pagehide-persisted" ||
|
|
1170
|
+
reason === "visibility-hidden");
|
|
1171
|
+
}
|
|
1160
1172
|
function flushReasonPriority(reason) {
|
|
1161
1173
|
if (reason === "pagehide" ||
|
|
1162
1174
|
reason === "pagehide-persisted" ||
|
|
@@ -1176,12 +1188,6 @@ function mergePendingFlushReason(current, requested) {
|
|
|
1176
1188
|
? requested
|
|
1177
1189
|
: current;
|
|
1178
1190
|
}
|
|
1179
|
-
function shouldReserveSequenceBeforeKeepalive(reason) {
|
|
1180
|
-
return (reason === "pagehide" ||
|
|
1181
|
-
reason === "pagehide-persisted" ||
|
|
1182
|
-
reason === "beforeunload" ||
|
|
1183
|
-
reason === "visibility-hidden");
|
|
1184
|
-
}
|
|
1185
1191
|
function hasFullSnapshot(events) {
|
|
1186
1192
|
return events.some((event) => event.type === RRWEB_FULL_SNAPSHOT_EVENT_TYPE);
|
|
1187
1193
|
}
|
|
@@ -1449,19 +1455,39 @@ export async function flushSessionReplay(reason = "manual") {
|
|
|
1449
1455
|
let pausedForQuota = false;
|
|
1450
1456
|
let quotaRetryAfterSeconds = null;
|
|
1451
1457
|
try {
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1458
|
+
// An upload can be stored after a navigation has destroyed this page and
|
|
1459
|
+
// its response handler: a keepalive one because the browser finishes
|
|
1460
|
+
// sending it, and a larger one when the server already has its body.
|
|
1461
|
+
// Persist the next index first, or the next page resends this chunk number
|
|
1462
|
+
// with different content and the server rejects it (HTTP 409). A flush as
|
|
1463
|
+
// the page hides or leaves never starts an upload too large for keepalive:
|
|
1464
|
+
// a navigation usually cancels it, so a reserved number would leave a gap,
|
|
1465
|
+
// and an unreserved one that still arrives would be reused. Its events stay
|
|
1466
|
+
// queued for the next flush of a page that survives. A same-origin body is
|
|
1467
|
+
// compressed before this check, so a hide or leave event can arrive in
|
|
1468
|
+
// between; it is queued as the pending reason.
|
|
1469
|
+
const outcome = await sendReplayUpload(state.options, payload.body, {
|
|
1470
|
+
beforeUpload: (keepalive) => {
|
|
1471
|
+
const pendingReason = state.pendingFlushReason;
|
|
1472
|
+
if (!keepalive &&
|
|
1473
|
+
(isPageLeaveReplayFlushReason(reason) ||
|
|
1474
|
+
(pendingReason !== null &&
|
|
1475
|
+
isPageLeaveReplayFlushReason(pendingReason)))) {
|
|
1476
|
+
return "defer";
|
|
1457
1477
|
}
|
|
1458
|
-
|
|
1478
|
+
advanceReplaySequence(state, payload);
|
|
1479
|
+
reservedSequence = true;
|
|
1480
|
+
return "send";
|
|
1481
|
+
},
|
|
1459
1482
|
});
|
|
1460
|
-
if (
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1483
|
+
if (outcome === "deferred") {
|
|
1484
|
+
restoreReplayEvents(state, events);
|
|
1485
|
+
}
|
|
1486
|
+
else {
|
|
1487
|
+
state.automaticConflictRestartAttempted = false;
|
|
1488
|
+
state.transientClientErrorFailures = 0;
|
|
1489
|
+
uploaded = true;
|
|
1490
|
+
}
|
|
1465
1491
|
}
|
|
1466
1492
|
catch (error) {
|
|
1467
1493
|
if (error instanceof ReplayUploadInFlightTimeoutError) {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { SHARE_LINK_REF_PARAM, SHARE_LINK_VIA_PARAM, withShareLinkAttribution, } from "./share-link-attribution.js";
|
|
2
|
+
export { useAccessRequestDecisions, useAccessRequestReview, useResourceAccessRequests, type AccessRequestDecisionFailure, type AccessRequestDecisions, type AccessRequestReview, type AccessRequestReviewController, type AccessRequestRole, type ResourceAccessRequestsController, } from "./useAccessRequestReview.js";
|
|
3
|
+
export { useResourceAccessGate, type ResourceAccessGateController, type ResourceAccessGateOptions, type ResourceAccessGateRequest, type ResourceAccessRequestFailure, type ResourceAccessGateRole, type ResourceAccessGateState, type ResourceAccessGateStatus, } from "./useResourceAccessGate.js";
|
|
2
4
|
export { useShareButtonController, type ShareButtonController, type ShareButtonControllerOptions, type ShareButtonOrgMember, type ShareButtonOrgMemberSearch, type ShareButtonRole, type ShareButtonShare, type ShareButtonSharesResponse, type ShareButtonVisibility, } from "./useShareButtonController.js";
|
|
3
5
|
export { useShareDialogController, type ResourceShare, type ResourceSharesResponse, type ShareDialogController, type ShareDialogControllerOptions, type ShareDialogPerson, type ShareDialogTab, type ShareOption, type ShareRole, type ShareVisibility, } from "./useShareDialogController.js";
|
|
4
6
|
export { createShareQueryKey, createShareQueryParams, extractShareErrorMessage, fetchOrgMemberPage, mergeOrgMembers, normalizeOrgMembers, optimisticallyUpdateShareCache, rollbackShareCache, useShareMutationGuard, useShareMutations, useShareOrgMemberSearch, useShareQuery, type OrgMemberPage, type ShareOrgMember, type ShareOrgMemberSearchResult, type ShareQueryKey, type ShareQueryParams, } from "./share-controller-helpers.js";
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { SHARE_LINK_REF_PARAM, SHARE_LINK_VIA_PARAM, withShareLinkAttribution, } from "./share-link-attribution.js";
|
|
2
|
+
export { useAccessRequestDecisions, useAccessRequestReview, useResourceAccessRequests, } from "./useAccessRequestReview.js";
|
|
3
|
+
export { useResourceAccessGate, } from "./useResourceAccessGate.js";
|
|
2
4
|
export { useShareButtonController, } from "./useShareButtonController.js";
|
|
3
5
|
export { useShareDialogController, } from "./useShareDialogController.js";
|
|
4
6
|
export { createShareQueryKey, createShareQueryParams, extractShareErrorMessage, fetchOrgMemberPage, mergeOrgMembers, normalizeOrgMembers, optimisticallyUpdateShareCache, rollbackShareCache, useShareMutationGuard, useShareMutations, useShareOrgMemberSearch, useShareQuery, } from "./share-controller-helpers.js";
|