@xenide-io/suite-client 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +193 -3
- package/dist/index.d.ts +193 -3
- package/dist/index.js +9810 -47
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +9794 -42
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ConfigDefaults } from 'posthog-js';
|
|
2
|
+
|
|
1
3
|
declare const ERROR_CODES: {
|
|
2
4
|
readonly AUTH_001: "E-AUTH-001";
|
|
3
5
|
readonly AUTH_002: "E-AUTH-002";
|
|
@@ -45,9 +47,54 @@ declare function normalizeThrown(input: unknown, capability?: string): AppError;
|
|
|
45
47
|
/** Display string for a failure surface: capability copy plus the code. */
|
|
46
48
|
declare function formatAppError(error: AppError): string;
|
|
47
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Global super-properties and event properties accepted by the analytics client.
|
|
52
|
+
* Values may be `null` to clear a previously registered super-property.
|
|
53
|
+
*/
|
|
54
|
+
type AnalyticsProperties = Record<string, unknown>;
|
|
55
|
+
/** Headers forwarded on every API request so server events share the web identity. */
|
|
56
|
+
type AnalyticsContextHeaders = Record<string, string>;
|
|
57
|
+
interface AnalyticsClientConfig {
|
|
58
|
+
/** `NEXT_PUBLIC_POSTHOG_KEY`. A missing key makes the client a no-op. */
|
|
59
|
+
apiKey?: string;
|
|
60
|
+
/** `NEXT_PUBLIC_POSTHOG_HOST`, e.g. `https://us.i.posthog.com`. */
|
|
61
|
+
host?: string;
|
|
62
|
+
/** Global `app` super-property, e.g. `turtletime`. */
|
|
63
|
+
app: string;
|
|
64
|
+
/** Global `environment` super-property; defaults to `NODE_ENV`. */
|
|
65
|
+
environment?: string;
|
|
66
|
+
/** PostHog config defaults date. Defaults to `2026-05-30`. */
|
|
67
|
+
defaults?: ConfigDefaults;
|
|
68
|
+
}
|
|
69
|
+
type AnalyticsClient = ReturnType<typeof createAnalyticsClient>;
|
|
70
|
+
/** Domain half of an email — safe to send; the raw address is not. */
|
|
71
|
+
declare function emailDomain(email: string | null | undefined): string | undefined;
|
|
72
|
+
/**
|
|
73
|
+
* Browser analytics client shared across ShellStack apps.
|
|
74
|
+
*
|
|
75
|
+
* Wraps `posthog-js` so every app initialises the same way, registers the same
|
|
76
|
+
* global super-properties, and forwards the browser identity/session ids to the
|
|
77
|
+
* API. The SDK keeps PostHog's defaults (autocapture, pageviews, UTM, browser/OS,
|
|
78
|
+
* referrer) — nothing here disables them.
|
|
79
|
+
*/
|
|
80
|
+
declare function createAnalyticsClient(config: AnalyticsClientConfig): {
|
|
81
|
+
init: () => void;
|
|
82
|
+
identify: (userId: string, props?: AnalyticsProperties, setOnceProps?: AnalyticsProperties) => void;
|
|
83
|
+
group: (groupType: string, groupKey: string, props?: AnalyticsProperties) => void;
|
|
84
|
+
capture: (event: string, props?: AnalyticsProperties) => void;
|
|
85
|
+
reset: () => void;
|
|
86
|
+
getDistinctId: () => string | undefined;
|
|
87
|
+
getSessionId: () => string | undefined;
|
|
88
|
+
pageview: () => void;
|
|
89
|
+
registerSuperProperties: (props: AnalyticsProperties) => void;
|
|
90
|
+
getContextHeaders: () => AnalyticsContextHeaders;
|
|
91
|
+
};
|
|
92
|
+
|
|
48
93
|
type ApiFetchInit = RequestInit & {
|
|
49
94
|
skipAuth?: boolean;
|
|
50
95
|
};
|
|
96
|
+
/** Optional analytics sink forwarded client lifecycle events (e.g. `analytics.capture`). */
|
|
97
|
+
type ApiAnalyticsEventSink = (event: string, properties?: Record<string, unknown>) => void;
|
|
51
98
|
interface ApiClientConfig {
|
|
52
99
|
/** localStorage key namespace, e.g. `tt` or `tides`. */
|
|
53
100
|
storagePrefix: string;
|
|
@@ -74,6 +121,18 @@ interface ApiClientConfig {
|
|
|
74
121
|
sessionStorageClearPrefixes?: string[];
|
|
75
122
|
/** Refuse to send the session to an unexpected absolute origin. */
|
|
76
123
|
enforceSameOrigin?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Extra headers to attach to every request. Used to forward the PostHog
|
|
126
|
+
* identity/session ids (`X-POSTHOG-DISTINCT-ID` / `X-POSTHOG-SESSION-ID`)
|
|
127
|
+
* so server-side events share the browser identity. Empty values are dropped.
|
|
128
|
+
*/
|
|
129
|
+
getContextHeaders?: () => Record<string, string>;
|
|
130
|
+
/**
|
|
131
|
+
* Forward client lifecycle events (`Session Expired`, `Workspace Switched`,
|
|
132
|
+
* `User Logged Out`) to the app's analytics client. Kept as a plain callback
|
|
133
|
+
* so this package never imports an analytics implementation.
|
|
134
|
+
*/
|
|
135
|
+
onAnalyticsEvent?: ApiAnalyticsEventSink;
|
|
77
136
|
}
|
|
78
137
|
/**
|
|
79
138
|
* Build a browser API client scoped to one ShellStack app. Every app shares the
|
|
@@ -92,7 +151,9 @@ declare function createApiClient(config: ApiClientConfig): {
|
|
|
92
151
|
}) => void;
|
|
93
152
|
getActiveWorkspaceId: () => string | null;
|
|
94
153
|
restoreWorkspace: () => void;
|
|
95
|
-
clearAuth: (
|
|
154
|
+
clearAuth: (options?: {
|
|
155
|
+
origin?: "user" | "session_expired";
|
|
156
|
+
}) => void;
|
|
96
157
|
restoreTokens: () => void;
|
|
97
158
|
refreshAccessToken: () => Promise<boolean>;
|
|
98
159
|
parseApiError: (res: Response) => Promise<string>;
|
|
@@ -104,7 +165,9 @@ declare function createApiClient(config: ApiClientConfig): {
|
|
|
104
165
|
delete: (path: string, init?: ApiFetchInit) => Promise<Response>;
|
|
105
166
|
};
|
|
106
167
|
restoreSession: () => void;
|
|
107
|
-
clearSession: (
|
|
168
|
+
clearSession: (options?: {
|
|
169
|
+
origin?: "user" | "session_expired";
|
|
170
|
+
}) => void;
|
|
108
171
|
};
|
|
109
172
|
type ApiClient = ReturnType<typeof createApiClient>;
|
|
110
173
|
type Api = ApiClient['api'];
|
|
@@ -117,6 +180,8 @@ interface AuthReturnPathConfig {
|
|
|
117
180
|
defaultNext?: string;
|
|
118
181
|
portalCallbackPath?: string;
|
|
119
182
|
redeemPath?: string;
|
|
183
|
+
/** Forward the completed cross-app switch to the app's analytics client. */
|
|
184
|
+
onAnalyticsEvent?: (event: string, properties?: Record<string, unknown>) => void;
|
|
120
185
|
}
|
|
121
186
|
interface PortalHandoffPayload {
|
|
122
187
|
code?: string;
|
|
@@ -262,6 +327,88 @@ declare function createWorkspaceAppearanceApi(config: WorkspaceAppearanceApiConf
|
|
|
262
327
|
cacheWorkspaceAppearanceLocally: (appearance: WorkspaceAppearance) => void;
|
|
263
328
|
};
|
|
264
329
|
|
|
330
|
+
/** How long a persisted read-model snapshot may be shown after a reload. */
|
|
331
|
+
declare const SUITE_QUERY_CACHE_MAX_AGE_MS: number;
|
|
332
|
+
type QueryKeyPart = string | readonly string[];
|
|
333
|
+
interface PersistableQuery {
|
|
334
|
+
queryKey: readonly unknown[];
|
|
335
|
+
state: {
|
|
336
|
+
status: string;
|
|
337
|
+
data: unknown;
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
interface PersistableQueryClient {
|
|
341
|
+
getQueryCache(): {
|
|
342
|
+
getAll(): PersistableQuery[];
|
|
343
|
+
subscribe(listener: () => void): () => void;
|
|
344
|
+
};
|
|
345
|
+
setQueryData(queryKey: readonly unknown[], data: unknown): unknown;
|
|
346
|
+
invalidateQueries(filters: {
|
|
347
|
+
queryKey: readonly unknown[];
|
|
348
|
+
refetchType: 'none';
|
|
349
|
+
}): Promise<unknown>;
|
|
350
|
+
}
|
|
351
|
+
interface PersistSuiteQueriesOptions {
|
|
352
|
+
/** localStorage key, including a version. */
|
|
353
|
+
storageKey: string;
|
|
354
|
+
/** Access-token localStorage key. The JWT `sub` is the user half of the buster. */
|
|
355
|
+
tokenKey: string;
|
|
356
|
+
/** Active workspace localStorage key. */
|
|
357
|
+
workspaceKey: string;
|
|
358
|
+
/**
|
|
359
|
+
* Query keys to keep. A string matches `queryKey[0]`. A list matches a prefix,
|
|
360
|
+
* so `['tt', 'tracker-catalog']` does not keep `['tt', 'time-entries']`.
|
|
361
|
+
*/
|
|
362
|
+
allow: readonly QueryKeyPart[];
|
|
363
|
+
maxAgeMs?: number;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* User and workspace identity for a snapshot. Empty when the tab is signed out,
|
|
367
|
+
* which refuses both restore and write.
|
|
368
|
+
*/
|
|
369
|
+
declare function suiteCacheBuster(tokenKey: string, workspaceKey: string): string;
|
|
370
|
+
declare function readJsonSnapshot<T>(key: string, buster: string, maxAgeMs: number): T | null;
|
|
371
|
+
declare function writeJsonSnapshot(key: string, buster: string, value: unknown): void;
|
|
372
|
+
/**
|
|
373
|
+
* Restore an allow-listed query cache, then keep writing it. Restored queries
|
|
374
|
+
* are marked stale so the next mount refetches without hiding the snapshot.
|
|
375
|
+
* Returns an unsubscribe.
|
|
376
|
+
*/
|
|
377
|
+
declare function persistSuiteQueries(queryClient: PersistableQueryClient, options: PersistSuiteQueriesOptions): () => void;
|
|
378
|
+
/** Per-app browser cache. Live operational keys are intentionally absent. */
|
|
379
|
+
declare const SUITE_APP_QUERY_CACHES: {
|
|
380
|
+
readonly kraken: {
|
|
381
|
+
readonly storageKey: "kraken-query-cache:v1";
|
|
382
|
+
readonly tokenKey: "kraken_access_token";
|
|
383
|
+
readonly workspaceKey: "kraken_active_workspace_id";
|
|
384
|
+
readonly allow: readonly ["kraken-sidebar"];
|
|
385
|
+
};
|
|
386
|
+
readonly shellstack: {
|
|
387
|
+
readonly storageKey: "shellstack-query-cache:v1";
|
|
388
|
+
readonly tokenKey: "portal_access_token";
|
|
389
|
+
readonly workspaceKey: "portal_active_workspace_id";
|
|
390
|
+
readonly allow: readonly ["calendar-events", "org-workspaces", "organisation", "org-team", "directory"];
|
|
391
|
+
};
|
|
392
|
+
readonly tides: {
|
|
393
|
+
readonly storageKey: "tides-query-cache:v1";
|
|
394
|
+
readonly tokenKey: "tides_access_token";
|
|
395
|
+
readonly workspaceKey: "tides_active_workspace_id";
|
|
396
|
+
readonly allow: readonly [readonly ["tides", "projects"], readonly ["tides", "clients"], readonly ["tides", "workspace-members"], readonly ["tides", "workspace-tags"], readonly ["tides", "project-statuses"]];
|
|
397
|
+
};
|
|
398
|
+
readonly turtletime: {
|
|
399
|
+
readonly storageKey: "tt-query-cache:v1";
|
|
400
|
+
readonly tokenKey: "tt_access_token";
|
|
401
|
+
readonly workspaceKey: "tt_active_workspace_id";
|
|
402
|
+
readonly allow: readonly [readonly ["tt", "tracker-catalog"], readonly ["tt", "projects"], readonly ["tt", "clients"], readonly ["tt", "categories"]];
|
|
403
|
+
};
|
|
404
|
+
readonly shelly: {
|
|
405
|
+
readonly storageKey: "shelly-query-cache:v1";
|
|
406
|
+
readonly tokenKey: "shelly_access_token";
|
|
407
|
+
readonly workspaceKey: "shelly_active_workspace_id";
|
|
408
|
+
readonly allow: readonly [readonly ["shelly", "skills"], readonly ["shelly", "crews"], readonly ["shelly", "mcp-servers"], readonly ["shelly", "models"]];
|
|
409
|
+
};
|
|
410
|
+
};
|
|
411
|
+
|
|
265
412
|
type CrossAppSyncTopic = 'time' | 'integration' | 'tasks';
|
|
266
413
|
interface CrossAppSyncConfig {
|
|
267
414
|
api: Api;
|
|
@@ -296,6 +443,37 @@ declare function createLiveRefresh(sync: CrossAppSync): {
|
|
|
296
443
|
hasPendingCrossAppSync: (topic: CrossAppSyncTopic | undefined, since: number) => boolean;
|
|
297
444
|
};
|
|
298
445
|
|
|
446
|
+
interface SuiteRevisionMap {
|
|
447
|
+
documents: number;
|
|
448
|
+
calendar: number;
|
|
449
|
+
notifications: number;
|
|
450
|
+
tasks: number;
|
|
451
|
+
time: number;
|
|
452
|
+
catalogue: number;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* First successful poll is the baseline and refreshes nothing. Later polls
|
|
456
|
+
* report only topics whose integer moved, so unchanged lists are not refetched.
|
|
457
|
+
*/
|
|
458
|
+
declare function nextRevisionTick(seen: Record<string, number>, ready: boolean, data: Partial<SuiteRevisionMap> | null): {
|
|
459
|
+
seen: Record<string, number>;
|
|
460
|
+
ready: boolean;
|
|
461
|
+
changed: string[];
|
|
462
|
+
};
|
|
463
|
+
/**
|
|
464
|
+
* Poll workspace revisions and report topics whose integer moved.
|
|
465
|
+
* The first successful read is the baseline, so a reload does not refetch
|
|
466
|
+
* everything the snapshot already painted.
|
|
467
|
+
*/
|
|
468
|
+
declare function useSuiteRevisions(options: {
|
|
469
|
+
enabled: boolean;
|
|
470
|
+
/** Workspace (or other) id. Changing it refetches, so the previous workspace cannot stay on screen. */
|
|
471
|
+
scope?: string;
|
|
472
|
+
load: () => Promise<Partial<SuiteRevisionMap> | null>;
|
|
473
|
+
onTopic: (topic: string) => void;
|
|
474
|
+
pollMs?: number;
|
|
475
|
+
}): void;
|
|
476
|
+
|
|
299
477
|
/** Cross-app (suite) notification from `/api/notifications/` — shared across ShellStack apps. */
|
|
300
478
|
interface SuiteNotification {
|
|
301
479
|
id: string;
|
|
@@ -356,6 +534,18 @@ declare function createWebPushClient(config: WebPushClientConfig): {
|
|
|
356
534
|
disable: () => Promise<void>;
|
|
357
535
|
};
|
|
358
536
|
|
|
537
|
+
interface EmailNotificationPreference {
|
|
538
|
+
enabled: boolean;
|
|
539
|
+
busy: boolean;
|
|
540
|
+
error: string;
|
|
541
|
+
onChange: (enabled: boolean) => void;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Loads and saves the signed-in person's email notification preference.
|
|
545
|
+
* The preference is shared across suite apps.
|
|
546
|
+
*/
|
|
547
|
+
declare function useEmailNotificationPreference(api: Api, parseApiError: (res: Response) => Promise<string>): EmailNotificationPreference;
|
|
548
|
+
|
|
359
549
|
type SuiteAiJobEvent = {
|
|
360
550
|
sequence: number;
|
|
361
551
|
event_type: 'queued' | 'started' | 'progress' | 'tool' | 'completed' | 'failed' | 'cancelled';
|
|
@@ -416,4 +606,4 @@ declare function createJsonLd(config: JsonLdConfig): string;
|
|
|
416
606
|
|
|
417
607
|
declare function useIsNight(): boolean;
|
|
418
608
|
|
|
419
|
-
export { type Api, type ApiClient, type ApiClientConfig, type ApiFetchInit, type AppError, type AuthReturnPath, type AuthReturnPathConfig, type BrandSettings, type BrandSettingsConfig, type CrossAppSync, type CrossAppSyncConfig, type CrossAppSyncTopic, DEFAULT_ERROR_CODE, ERROR_CODES, ERROR_COPY, ERROR_RETRYABLE, type ErrorCode, FREE_PLAN_SEAT_HELP, type JsonLdConfig, type LiveRefresh, type LiveRefreshOptions, type NotificationsClient, type NotificationsClientConfig, ORGANISATION_SEAT_HELP, type PortalHandoffPayload, type PortalHandoffRead, type SeatType, type SubscriptionTier, type SuiteAiJob, type SuiteAiJobEvent, type SuiteNotification, type SuiteNotificationsResponse, TIDES_ACCESS_HELP, TURTLETIME_ACCESS_HELP, WEBHOOK_ACTIVITY_ACTIONS, WORKSPACE_SEAT_HELP, type WebPushClient, type WebPushClientConfig, type WebPushConfig, type WebPushState, type WorkspaceAppearance, type WorkspaceAppearanceApiConfig, type WorkspaceAppearanceConfig, WorkspaceAppearanceError, type WorkspaceAppearanceHelpers, type WorkspaceAppearancePatch, type WorkspaceBrandPayload, type WorkspaceSubscription, type WorkspaceTheme, activeSuiteAiJobs, appErrorFromResponse, canDisableAppAccess, codeForStatus, copyFor, createApiClient, createAuthReturnPath, createBrandSettings, createCrossAppSync, createJsonLd, createLiveRefresh, createNotificationsClient, createWebPushClient, createWorkspaceAppearance, createWorkspaceAppearanceApi, defaultInviteSeatType, flagsFromSeatType, formatAppError, hasAppAccess, isErrorCode, normalizeError, normalizeThrown, planSeatSummary, resolveSubscriptionTier, seatTypeDescription, seatTypeFromFlags, seatTypeLabel, selectedToWebhookActions, sortedSuiteAiEvents, suiteAiEventSummary, tierAllowsTidesOnlySeats, tierAllowsTurtletime, tierLabel, useIsNight, webhookActionsToSelected };
|
|
609
|
+
export { type AnalyticsClient, type AnalyticsClientConfig, type AnalyticsContextHeaders, type AnalyticsProperties, type Api, type ApiAnalyticsEventSink, type ApiClient, type ApiClientConfig, type ApiFetchInit, type AppError, type AuthReturnPath, type AuthReturnPathConfig, type BrandSettings, type BrandSettingsConfig, type CrossAppSync, type CrossAppSyncConfig, type CrossAppSyncTopic, DEFAULT_ERROR_CODE, ERROR_CODES, ERROR_COPY, ERROR_RETRYABLE, type EmailNotificationPreference, type ErrorCode, FREE_PLAN_SEAT_HELP, type JsonLdConfig, type LiveRefresh, type LiveRefreshOptions, type NotificationsClient, type NotificationsClientConfig, ORGANISATION_SEAT_HELP, type PersistSuiteQueriesOptions, type PersistableQuery, type PersistableQueryClient, type PortalHandoffPayload, type PortalHandoffRead, type QueryKeyPart, SUITE_APP_QUERY_CACHES, SUITE_QUERY_CACHE_MAX_AGE_MS, type SeatType, type SubscriptionTier, type SuiteAiJob, type SuiteAiJobEvent, type SuiteNotification, type SuiteNotificationsResponse, type SuiteRevisionMap, TIDES_ACCESS_HELP, TURTLETIME_ACCESS_HELP, WEBHOOK_ACTIVITY_ACTIONS, WORKSPACE_SEAT_HELP, type WebPushClient, type WebPushClientConfig, type WebPushConfig, type WebPushState, type WorkspaceAppearance, type WorkspaceAppearanceApiConfig, type WorkspaceAppearanceConfig, WorkspaceAppearanceError, type WorkspaceAppearanceHelpers, type WorkspaceAppearancePatch, type WorkspaceBrandPayload, type WorkspaceSubscription, type WorkspaceTheme, activeSuiteAiJobs, appErrorFromResponse, canDisableAppAccess, codeForStatus, copyFor, createAnalyticsClient, createApiClient, createAuthReturnPath, createBrandSettings, createCrossAppSync, createJsonLd, createLiveRefresh, createNotificationsClient, createWebPushClient, createWorkspaceAppearance, createWorkspaceAppearanceApi, defaultInviteSeatType, emailDomain, flagsFromSeatType, formatAppError, hasAppAccess, isErrorCode, nextRevisionTick, normalizeError, normalizeThrown, persistSuiteQueries, planSeatSummary, readJsonSnapshot, resolveSubscriptionTier, seatTypeDescription, seatTypeFromFlags, seatTypeLabel, selectedToWebhookActions, sortedSuiteAiEvents, suiteAiEventSummary, suiteCacheBuster, tierAllowsTidesOnlySeats, tierAllowsTurtletime, tierLabel, useEmailNotificationPreference, useIsNight, useSuiteRevisions, webhookActionsToSelected, writeJsonSnapshot };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ConfigDefaults } from 'posthog-js';
|
|
2
|
+
|
|
1
3
|
declare const ERROR_CODES: {
|
|
2
4
|
readonly AUTH_001: "E-AUTH-001";
|
|
3
5
|
readonly AUTH_002: "E-AUTH-002";
|
|
@@ -45,9 +47,54 @@ declare function normalizeThrown(input: unknown, capability?: string): AppError;
|
|
|
45
47
|
/** Display string for a failure surface: capability copy plus the code. */
|
|
46
48
|
declare function formatAppError(error: AppError): string;
|
|
47
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Global super-properties and event properties accepted by the analytics client.
|
|
52
|
+
* Values may be `null` to clear a previously registered super-property.
|
|
53
|
+
*/
|
|
54
|
+
type AnalyticsProperties = Record<string, unknown>;
|
|
55
|
+
/** Headers forwarded on every API request so server events share the web identity. */
|
|
56
|
+
type AnalyticsContextHeaders = Record<string, string>;
|
|
57
|
+
interface AnalyticsClientConfig {
|
|
58
|
+
/** `NEXT_PUBLIC_POSTHOG_KEY`. A missing key makes the client a no-op. */
|
|
59
|
+
apiKey?: string;
|
|
60
|
+
/** `NEXT_PUBLIC_POSTHOG_HOST`, e.g. `https://us.i.posthog.com`. */
|
|
61
|
+
host?: string;
|
|
62
|
+
/** Global `app` super-property, e.g. `turtletime`. */
|
|
63
|
+
app: string;
|
|
64
|
+
/** Global `environment` super-property; defaults to `NODE_ENV`. */
|
|
65
|
+
environment?: string;
|
|
66
|
+
/** PostHog config defaults date. Defaults to `2026-05-30`. */
|
|
67
|
+
defaults?: ConfigDefaults;
|
|
68
|
+
}
|
|
69
|
+
type AnalyticsClient = ReturnType<typeof createAnalyticsClient>;
|
|
70
|
+
/** Domain half of an email — safe to send; the raw address is not. */
|
|
71
|
+
declare function emailDomain(email: string | null | undefined): string | undefined;
|
|
72
|
+
/**
|
|
73
|
+
* Browser analytics client shared across ShellStack apps.
|
|
74
|
+
*
|
|
75
|
+
* Wraps `posthog-js` so every app initialises the same way, registers the same
|
|
76
|
+
* global super-properties, and forwards the browser identity/session ids to the
|
|
77
|
+
* API. The SDK keeps PostHog's defaults (autocapture, pageviews, UTM, browser/OS,
|
|
78
|
+
* referrer) — nothing here disables them.
|
|
79
|
+
*/
|
|
80
|
+
declare function createAnalyticsClient(config: AnalyticsClientConfig): {
|
|
81
|
+
init: () => void;
|
|
82
|
+
identify: (userId: string, props?: AnalyticsProperties, setOnceProps?: AnalyticsProperties) => void;
|
|
83
|
+
group: (groupType: string, groupKey: string, props?: AnalyticsProperties) => void;
|
|
84
|
+
capture: (event: string, props?: AnalyticsProperties) => void;
|
|
85
|
+
reset: () => void;
|
|
86
|
+
getDistinctId: () => string | undefined;
|
|
87
|
+
getSessionId: () => string | undefined;
|
|
88
|
+
pageview: () => void;
|
|
89
|
+
registerSuperProperties: (props: AnalyticsProperties) => void;
|
|
90
|
+
getContextHeaders: () => AnalyticsContextHeaders;
|
|
91
|
+
};
|
|
92
|
+
|
|
48
93
|
type ApiFetchInit = RequestInit & {
|
|
49
94
|
skipAuth?: boolean;
|
|
50
95
|
};
|
|
96
|
+
/** Optional analytics sink forwarded client lifecycle events (e.g. `analytics.capture`). */
|
|
97
|
+
type ApiAnalyticsEventSink = (event: string, properties?: Record<string, unknown>) => void;
|
|
51
98
|
interface ApiClientConfig {
|
|
52
99
|
/** localStorage key namespace, e.g. `tt` or `tides`. */
|
|
53
100
|
storagePrefix: string;
|
|
@@ -74,6 +121,18 @@ interface ApiClientConfig {
|
|
|
74
121
|
sessionStorageClearPrefixes?: string[];
|
|
75
122
|
/** Refuse to send the session to an unexpected absolute origin. */
|
|
76
123
|
enforceSameOrigin?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Extra headers to attach to every request. Used to forward the PostHog
|
|
126
|
+
* identity/session ids (`X-POSTHOG-DISTINCT-ID` / `X-POSTHOG-SESSION-ID`)
|
|
127
|
+
* so server-side events share the browser identity. Empty values are dropped.
|
|
128
|
+
*/
|
|
129
|
+
getContextHeaders?: () => Record<string, string>;
|
|
130
|
+
/**
|
|
131
|
+
* Forward client lifecycle events (`Session Expired`, `Workspace Switched`,
|
|
132
|
+
* `User Logged Out`) to the app's analytics client. Kept as a plain callback
|
|
133
|
+
* so this package never imports an analytics implementation.
|
|
134
|
+
*/
|
|
135
|
+
onAnalyticsEvent?: ApiAnalyticsEventSink;
|
|
77
136
|
}
|
|
78
137
|
/**
|
|
79
138
|
* Build a browser API client scoped to one ShellStack app. Every app shares the
|
|
@@ -92,7 +151,9 @@ declare function createApiClient(config: ApiClientConfig): {
|
|
|
92
151
|
}) => void;
|
|
93
152
|
getActiveWorkspaceId: () => string | null;
|
|
94
153
|
restoreWorkspace: () => void;
|
|
95
|
-
clearAuth: (
|
|
154
|
+
clearAuth: (options?: {
|
|
155
|
+
origin?: "user" | "session_expired";
|
|
156
|
+
}) => void;
|
|
96
157
|
restoreTokens: () => void;
|
|
97
158
|
refreshAccessToken: () => Promise<boolean>;
|
|
98
159
|
parseApiError: (res: Response) => Promise<string>;
|
|
@@ -104,7 +165,9 @@ declare function createApiClient(config: ApiClientConfig): {
|
|
|
104
165
|
delete: (path: string, init?: ApiFetchInit) => Promise<Response>;
|
|
105
166
|
};
|
|
106
167
|
restoreSession: () => void;
|
|
107
|
-
clearSession: (
|
|
168
|
+
clearSession: (options?: {
|
|
169
|
+
origin?: "user" | "session_expired";
|
|
170
|
+
}) => void;
|
|
108
171
|
};
|
|
109
172
|
type ApiClient = ReturnType<typeof createApiClient>;
|
|
110
173
|
type Api = ApiClient['api'];
|
|
@@ -117,6 +180,8 @@ interface AuthReturnPathConfig {
|
|
|
117
180
|
defaultNext?: string;
|
|
118
181
|
portalCallbackPath?: string;
|
|
119
182
|
redeemPath?: string;
|
|
183
|
+
/** Forward the completed cross-app switch to the app's analytics client. */
|
|
184
|
+
onAnalyticsEvent?: (event: string, properties?: Record<string, unknown>) => void;
|
|
120
185
|
}
|
|
121
186
|
interface PortalHandoffPayload {
|
|
122
187
|
code?: string;
|
|
@@ -262,6 +327,88 @@ declare function createWorkspaceAppearanceApi(config: WorkspaceAppearanceApiConf
|
|
|
262
327
|
cacheWorkspaceAppearanceLocally: (appearance: WorkspaceAppearance) => void;
|
|
263
328
|
};
|
|
264
329
|
|
|
330
|
+
/** How long a persisted read-model snapshot may be shown after a reload. */
|
|
331
|
+
declare const SUITE_QUERY_CACHE_MAX_AGE_MS: number;
|
|
332
|
+
type QueryKeyPart = string | readonly string[];
|
|
333
|
+
interface PersistableQuery {
|
|
334
|
+
queryKey: readonly unknown[];
|
|
335
|
+
state: {
|
|
336
|
+
status: string;
|
|
337
|
+
data: unknown;
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
interface PersistableQueryClient {
|
|
341
|
+
getQueryCache(): {
|
|
342
|
+
getAll(): PersistableQuery[];
|
|
343
|
+
subscribe(listener: () => void): () => void;
|
|
344
|
+
};
|
|
345
|
+
setQueryData(queryKey: readonly unknown[], data: unknown): unknown;
|
|
346
|
+
invalidateQueries(filters: {
|
|
347
|
+
queryKey: readonly unknown[];
|
|
348
|
+
refetchType: 'none';
|
|
349
|
+
}): Promise<unknown>;
|
|
350
|
+
}
|
|
351
|
+
interface PersistSuiteQueriesOptions {
|
|
352
|
+
/** localStorage key, including a version. */
|
|
353
|
+
storageKey: string;
|
|
354
|
+
/** Access-token localStorage key. The JWT `sub` is the user half of the buster. */
|
|
355
|
+
tokenKey: string;
|
|
356
|
+
/** Active workspace localStorage key. */
|
|
357
|
+
workspaceKey: string;
|
|
358
|
+
/**
|
|
359
|
+
* Query keys to keep. A string matches `queryKey[0]`. A list matches a prefix,
|
|
360
|
+
* so `['tt', 'tracker-catalog']` does not keep `['tt', 'time-entries']`.
|
|
361
|
+
*/
|
|
362
|
+
allow: readonly QueryKeyPart[];
|
|
363
|
+
maxAgeMs?: number;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* User and workspace identity for a snapshot. Empty when the tab is signed out,
|
|
367
|
+
* which refuses both restore and write.
|
|
368
|
+
*/
|
|
369
|
+
declare function suiteCacheBuster(tokenKey: string, workspaceKey: string): string;
|
|
370
|
+
declare function readJsonSnapshot<T>(key: string, buster: string, maxAgeMs: number): T | null;
|
|
371
|
+
declare function writeJsonSnapshot(key: string, buster: string, value: unknown): void;
|
|
372
|
+
/**
|
|
373
|
+
* Restore an allow-listed query cache, then keep writing it. Restored queries
|
|
374
|
+
* are marked stale so the next mount refetches without hiding the snapshot.
|
|
375
|
+
* Returns an unsubscribe.
|
|
376
|
+
*/
|
|
377
|
+
declare function persistSuiteQueries(queryClient: PersistableQueryClient, options: PersistSuiteQueriesOptions): () => void;
|
|
378
|
+
/** Per-app browser cache. Live operational keys are intentionally absent. */
|
|
379
|
+
declare const SUITE_APP_QUERY_CACHES: {
|
|
380
|
+
readonly kraken: {
|
|
381
|
+
readonly storageKey: "kraken-query-cache:v1";
|
|
382
|
+
readonly tokenKey: "kraken_access_token";
|
|
383
|
+
readonly workspaceKey: "kraken_active_workspace_id";
|
|
384
|
+
readonly allow: readonly ["kraken-sidebar"];
|
|
385
|
+
};
|
|
386
|
+
readonly shellstack: {
|
|
387
|
+
readonly storageKey: "shellstack-query-cache:v1";
|
|
388
|
+
readonly tokenKey: "portal_access_token";
|
|
389
|
+
readonly workspaceKey: "portal_active_workspace_id";
|
|
390
|
+
readonly allow: readonly ["calendar-events", "org-workspaces", "organisation", "org-team", "directory"];
|
|
391
|
+
};
|
|
392
|
+
readonly tides: {
|
|
393
|
+
readonly storageKey: "tides-query-cache:v1";
|
|
394
|
+
readonly tokenKey: "tides_access_token";
|
|
395
|
+
readonly workspaceKey: "tides_active_workspace_id";
|
|
396
|
+
readonly allow: readonly [readonly ["tides", "projects"], readonly ["tides", "clients"], readonly ["tides", "workspace-members"], readonly ["tides", "workspace-tags"], readonly ["tides", "project-statuses"]];
|
|
397
|
+
};
|
|
398
|
+
readonly turtletime: {
|
|
399
|
+
readonly storageKey: "tt-query-cache:v1";
|
|
400
|
+
readonly tokenKey: "tt_access_token";
|
|
401
|
+
readonly workspaceKey: "tt_active_workspace_id";
|
|
402
|
+
readonly allow: readonly [readonly ["tt", "tracker-catalog"], readonly ["tt", "projects"], readonly ["tt", "clients"], readonly ["tt", "categories"]];
|
|
403
|
+
};
|
|
404
|
+
readonly shelly: {
|
|
405
|
+
readonly storageKey: "shelly-query-cache:v1";
|
|
406
|
+
readonly tokenKey: "shelly_access_token";
|
|
407
|
+
readonly workspaceKey: "shelly_active_workspace_id";
|
|
408
|
+
readonly allow: readonly [readonly ["shelly", "skills"], readonly ["shelly", "crews"], readonly ["shelly", "mcp-servers"], readonly ["shelly", "models"]];
|
|
409
|
+
};
|
|
410
|
+
};
|
|
411
|
+
|
|
265
412
|
type CrossAppSyncTopic = 'time' | 'integration' | 'tasks';
|
|
266
413
|
interface CrossAppSyncConfig {
|
|
267
414
|
api: Api;
|
|
@@ -296,6 +443,37 @@ declare function createLiveRefresh(sync: CrossAppSync): {
|
|
|
296
443
|
hasPendingCrossAppSync: (topic: CrossAppSyncTopic | undefined, since: number) => boolean;
|
|
297
444
|
};
|
|
298
445
|
|
|
446
|
+
interface SuiteRevisionMap {
|
|
447
|
+
documents: number;
|
|
448
|
+
calendar: number;
|
|
449
|
+
notifications: number;
|
|
450
|
+
tasks: number;
|
|
451
|
+
time: number;
|
|
452
|
+
catalogue: number;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* First successful poll is the baseline and refreshes nothing. Later polls
|
|
456
|
+
* report only topics whose integer moved, so unchanged lists are not refetched.
|
|
457
|
+
*/
|
|
458
|
+
declare function nextRevisionTick(seen: Record<string, number>, ready: boolean, data: Partial<SuiteRevisionMap> | null): {
|
|
459
|
+
seen: Record<string, number>;
|
|
460
|
+
ready: boolean;
|
|
461
|
+
changed: string[];
|
|
462
|
+
};
|
|
463
|
+
/**
|
|
464
|
+
* Poll workspace revisions and report topics whose integer moved.
|
|
465
|
+
* The first successful read is the baseline, so a reload does not refetch
|
|
466
|
+
* everything the snapshot already painted.
|
|
467
|
+
*/
|
|
468
|
+
declare function useSuiteRevisions(options: {
|
|
469
|
+
enabled: boolean;
|
|
470
|
+
/** Workspace (or other) id. Changing it refetches, so the previous workspace cannot stay on screen. */
|
|
471
|
+
scope?: string;
|
|
472
|
+
load: () => Promise<Partial<SuiteRevisionMap> | null>;
|
|
473
|
+
onTopic: (topic: string) => void;
|
|
474
|
+
pollMs?: number;
|
|
475
|
+
}): void;
|
|
476
|
+
|
|
299
477
|
/** Cross-app (suite) notification from `/api/notifications/` — shared across ShellStack apps. */
|
|
300
478
|
interface SuiteNotification {
|
|
301
479
|
id: string;
|
|
@@ -356,6 +534,18 @@ declare function createWebPushClient(config: WebPushClientConfig): {
|
|
|
356
534
|
disable: () => Promise<void>;
|
|
357
535
|
};
|
|
358
536
|
|
|
537
|
+
interface EmailNotificationPreference {
|
|
538
|
+
enabled: boolean;
|
|
539
|
+
busy: boolean;
|
|
540
|
+
error: string;
|
|
541
|
+
onChange: (enabled: boolean) => void;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Loads and saves the signed-in person's email notification preference.
|
|
545
|
+
* The preference is shared across suite apps.
|
|
546
|
+
*/
|
|
547
|
+
declare function useEmailNotificationPreference(api: Api, parseApiError: (res: Response) => Promise<string>): EmailNotificationPreference;
|
|
548
|
+
|
|
359
549
|
type SuiteAiJobEvent = {
|
|
360
550
|
sequence: number;
|
|
361
551
|
event_type: 'queued' | 'started' | 'progress' | 'tool' | 'completed' | 'failed' | 'cancelled';
|
|
@@ -416,4 +606,4 @@ declare function createJsonLd(config: JsonLdConfig): string;
|
|
|
416
606
|
|
|
417
607
|
declare function useIsNight(): boolean;
|
|
418
608
|
|
|
419
|
-
export { type Api, type ApiClient, type ApiClientConfig, type ApiFetchInit, type AppError, type AuthReturnPath, type AuthReturnPathConfig, type BrandSettings, type BrandSettingsConfig, type CrossAppSync, type CrossAppSyncConfig, type CrossAppSyncTopic, DEFAULT_ERROR_CODE, ERROR_CODES, ERROR_COPY, ERROR_RETRYABLE, type ErrorCode, FREE_PLAN_SEAT_HELP, type JsonLdConfig, type LiveRefresh, type LiveRefreshOptions, type NotificationsClient, type NotificationsClientConfig, ORGANISATION_SEAT_HELP, type PortalHandoffPayload, type PortalHandoffRead, type SeatType, type SubscriptionTier, type SuiteAiJob, type SuiteAiJobEvent, type SuiteNotification, type SuiteNotificationsResponse, TIDES_ACCESS_HELP, TURTLETIME_ACCESS_HELP, WEBHOOK_ACTIVITY_ACTIONS, WORKSPACE_SEAT_HELP, type WebPushClient, type WebPushClientConfig, type WebPushConfig, type WebPushState, type WorkspaceAppearance, type WorkspaceAppearanceApiConfig, type WorkspaceAppearanceConfig, WorkspaceAppearanceError, type WorkspaceAppearanceHelpers, type WorkspaceAppearancePatch, type WorkspaceBrandPayload, type WorkspaceSubscription, type WorkspaceTheme, activeSuiteAiJobs, appErrorFromResponse, canDisableAppAccess, codeForStatus, copyFor, createApiClient, createAuthReturnPath, createBrandSettings, createCrossAppSync, createJsonLd, createLiveRefresh, createNotificationsClient, createWebPushClient, createWorkspaceAppearance, createWorkspaceAppearanceApi, defaultInviteSeatType, flagsFromSeatType, formatAppError, hasAppAccess, isErrorCode, normalizeError, normalizeThrown, planSeatSummary, resolveSubscriptionTier, seatTypeDescription, seatTypeFromFlags, seatTypeLabel, selectedToWebhookActions, sortedSuiteAiEvents, suiteAiEventSummary, tierAllowsTidesOnlySeats, tierAllowsTurtletime, tierLabel, useIsNight, webhookActionsToSelected };
|
|
609
|
+
export { type AnalyticsClient, type AnalyticsClientConfig, type AnalyticsContextHeaders, type AnalyticsProperties, type Api, type ApiAnalyticsEventSink, type ApiClient, type ApiClientConfig, type ApiFetchInit, type AppError, type AuthReturnPath, type AuthReturnPathConfig, type BrandSettings, type BrandSettingsConfig, type CrossAppSync, type CrossAppSyncConfig, type CrossAppSyncTopic, DEFAULT_ERROR_CODE, ERROR_CODES, ERROR_COPY, ERROR_RETRYABLE, type EmailNotificationPreference, type ErrorCode, FREE_PLAN_SEAT_HELP, type JsonLdConfig, type LiveRefresh, type LiveRefreshOptions, type NotificationsClient, type NotificationsClientConfig, ORGANISATION_SEAT_HELP, type PersistSuiteQueriesOptions, type PersistableQuery, type PersistableQueryClient, type PortalHandoffPayload, type PortalHandoffRead, type QueryKeyPart, SUITE_APP_QUERY_CACHES, SUITE_QUERY_CACHE_MAX_AGE_MS, type SeatType, type SubscriptionTier, type SuiteAiJob, type SuiteAiJobEvent, type SuiteNotification, type SuiteNotificationsResponse, type SuiteRevisionMap, TIDES_ACCESS_HELP, TURTLETIME_ACCESS_HELP, WEBHOOK_ACTIVITY_ACTIONS, WORKSPACE_SEAT_HELP, type WebPushClient, type WebPushClientConfig, type WebPushConfig, type WebPushState, type WorkspaceAppearance, type WorkspaceAppearanceApiConfig, type WorkspaceAppearanceConfig, WorkspaceAppearanceError, type WorkspaceAppearanceHelpers, type WorkspaceAppearancePatch, type WorkspaceBrandPayload, type WorkspaceSubscription, type WorkspaceTheme, activeSuiteAiJobs, appErrorFromResponse, canDisableAppAccess, codeForStatus, copyFor, createAnalyticsClient, createApiClient, createAuthReturnPath, createBrandSettings, createCrossAppSync, createJsonLd, createLiveRefresh, createNotificationsClient, createWebPushClient, createWorkspaceAppearance, createWorkspaceAppearanceApi, defaultInviteSeatType, emailDomain, flagsFromSeatType, formatAppError, hasAppAccess, isErrorCode, nextRevisionTick, normalizeError, normalizeThrown, persistSuiteQueries, planSeatSummary, readJsonSnapshot, resolveSubscriptionTier, seatTypeDescription, seatTypeFromFlags, seatTypeLabel, selectedToWebhookActions, sortedSuiteAiEvents, suiteAiEventSummary, suiteCacheBuster, tierAllowsTidesOnlySeats, tierAllowsTurtletime, tierLabel, useEmailNotificationPreference, useIsNight, useSuiteRevisions, webhookActionsToSelected, writeJsonSnapshot };
|