@ductape/mcp 0.1.49 → 0.1.50

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.js CHANGED
@@ -1192,7 +1192,7 @@ const docsInputSchema = z.object({
1192
1192
  topic: z.string().describe('Feature topic to look up. Supported: ' +
1193
1193
  'transactions, presave, triggers, aggregations, migrations, indexes, performance, actions, ' +
1194
1194
  'graphs, storage, cloud, vector, warehouse, secrets, apps, products, sessions, caches, ' +
1195
- 'notifications, resilience, features, events, logs, frontend, client, react, vue'),
1195
+ 'notifications, resilience, features, events, logs, frontend, frontend-analytics, client, react, vue'),
1196
1196
  });
1197
1197
  const DOCS = {
1198
1198
  frontend: `
@@ -1228,6 +1228,11 @@ SHARED APPLICATION LIFECYCLE
1228
1228
  connectionState to render disconnected/reconnecting UI; do not create duplicate subscriptions.
1229
1229
  7. Disconnect resource sessions and the root client when the owning application scope is torn down.
1230
1230
 
1231
+ PRODUCT ANALYTICS
1232
+ Frontend product analytics complements backend session propagation; neither replaces the other.
1233
+ Continue with ductape_docs({ topic: "frontend-analytics" }) for identity lifecycle, pageviews,
1234
+ custom events, privacy masking, hidden-state safety, trace correlation, and package-version checks.
1235
+
1231
1236
  AUTHENTICATION AND SECURITY
1232
1237
  - Use publishableKey in browser applications. Never ship workspace private keys or privileged
1233
1238
  access keys in frontend bundles.
@@ -1973,6 +1978,7 @@ Resource registration (all via ductape_cli or ductape_execute — see per-topic
1973
1978
  caches → ductape_docs({ topic: "caches" })
1974
1979
  notifications → ductape_docs({ topic: "notifications" })
1975
1980
  sessions → ductape_docs({ topic: "sessions" })
1981
+ frontend analytics → ductape_docs({ topic: "frontend-analytics" })
1976
1982
  resilience → ductape_docs({ topic: "resilience" })
1977
1983
  features → ductape_docs({ topic: "features" })
1978
1984
 
@@ -2060,6 +2066,16 @@ Analytics:
2060
2066
 
2061
2067
  Token format: "session_tag:jwt_token" — pass this format verbatim wherever session is expected.
2062
2068
  JWT is signed with the product private key (not a symmetric shared secret).
2069
+
2070
+ SESSION PROPAGATION
2071
+ Preserve the original full token on every user-context database, Event, Feature, notification,
2072
+ storage, graph, vector, action, and other runtime operation that accepts session. Do not replace
2073
+ it with the session tag, user ID, decoded claims, or refresh token. Durable user-initiated work
2074
+ should retain the initiating token; intentional system/background work should be sessionless.
2075
+
2076
+ Backend propagation attributes component activity to an actor, but it does not record frontend
2077
+ pageviews, navigation, UI intent, funnels, or client failures. Continue immediately with
2078
+ ductape_docs({ topic: "frontend-analytics" }); both layers are required for a complete picture.
2063
2079
  `.trim(),
2064
2080
  caches: `
2065
2081
  DUCTAPE CACHES
@@ -2222,6 +2238,222 @@ Failure actions: notification channels, HTTP webhooks, and/or message broker emi
2222
2238
  be configured simultaneously on the same healthcheck.
2223
2239
  Input template references: $Input{field} → maps declared input to the probe's action input.
2224
2240
  Provider status: available | unavailable
2241
+ `.trim(),
2242
+ 'frontend-analytics': `
2243
+ DUCTAPE FRONTEND PRODUCT ANALYTICS AND SESSION ACTIVITY
2244
+
2245
+ Frontend analytics and backend session attribution are complementary; neither replaces the other.
2246
+
2247
+ BACKEND OPERATION ATTRIBUTION
2248
+ Pass the original full Ductape session token to every user-initiated database, Event, Feature,
2249
+ notification, storage, graph, vector, action, and other runtime operation that accepts session.
2250
+ This attributes server work to the authenticated actor. Durable work should retain the initiating
2251
+ session; intentional system/background work should remain sessionless.
2252
+
2253
+ FRONTEND PRODUCT ANALYTICS
2254
+ Use the browser Analytics service for anonymous visits, authenticated page views, navigation,
2255
+ UI interactions, funnels, client errors, realtime state, and product-surface engagement.
2256
+
2257
+ IMPORTANT:
2258
+ Passing session to backend component calls does not replace frontend analytics.
2259
+ Calling analytics.identify(sessionToken) and tracking frontend events does not replace backend
2260
+ session propagation. A complete activity picture requires both.
2261
+
2262
+ VERSION AND DOCUMENTATION PRECEDENCE
2263
+ 1. Installed package types and exports determine what can be called now.
2264
+ 2. Version-matched package documentation explains intended usage.
2265
+ 3. Current docs/docs/Frontend describes the latest supported design.
2266
+ 4. If they disagree, report the mismatch; never fabricate compatibility.
2267
+
2268
+ Inspect package.json plus package exports/type declarations before recommending framework hooks.
2269
+ The current repository exports useAnalytics from @ductape/react and @ductape/vue. Their installed
2270
+ hook/composable exposes track, pageview, identify, visitorId, enableAutoCapture, and flush.
2271
+ clearSession and disableAutoCapture are available on client.analytics but are not currently
2272
+ returned by those framework wrappers. Use useDuctape().client.analytics for those calls, or
2273
+ recommend a package version that exports them; do not generate a hook method that does not exist.
2274
+
2275
+ CLIENT API — IDENTIFY AFTER AUTHENTICATION
2276
+ ductape.analytics.identify(sessionToken);
2277
+
2278
+ sessionToken is the complete value returned by Ductape, in "player-session:jwt" format.
2279
+ It is NOT a player ID, session tag, session ID, decoded claims, or refresh token.
2280
+ identify links subsequent frontend analytics to the authenticated Ductape session and lets
2281
+ supported analytics correlate anonymous pre-login activity with authenticated activity.
2282
+
2283
+ LOGOUT / ACCOUNT SWITCHING
2284
+ await ductape.analytics.flush();
2285
+ ductape.analytics.clearSession();
2286
+
2287
+ Clear analytics identity when logout or revocation succeeds, refresh fails irrecoverably, local
2288
+ authentication is removed, or a different user is about to authenticate in the same browser.
2289
+ Removing only the application token can leave later anonymous/next-user events associated with
2290
+ the previous analytics identity. Flush is best-effort; browser shutdown does not guarantee it.
2291
+
2292
+ CUSTOM EVENTS
2293
+ await ductape.analytics.track({
2294
+ event: 'order_submitted',
2295
+ traceId,
2296
+ properties: { matchId, orderType },
2297
+ });
2298
+
2299
+ IAnalyticsTrackOptions:
2300
+ event: string
2301
+ properties?: Record<string, unknown>
2302
+ session?: string
2303
+ product?: string
2304
+ env?: string
2305
+ traceId?: string
2306
+ context?: { url?, path?, referrer?, locale?, screen?: { width, height } }
2307
+
2308
+ identify establishes the default analytics session. An individual event may explicitly provide
2309
+ session. Normally use product/env from client configuration. traceId correlates frontend intent
2310
+ with backend logs, Events, Features, and order processing.
2311
+
2312
+ PAGE VIEWS
2313
+ await ductape.analytics.pageview({
2314
+ path: location.pathname,
2315
+ title: document.title,
2316
+ properties: { matchId, screen: 'governance' },
2317
+ });
2318
+
2319
+ IAnalyticsPageviewOptions:
2320
+ path?: string
2321
+ title?: string
2322
+ session?: string
2323
+ product?: string
2324
+ env?: string
2325
+ properties?: Record<string, unknown>
2326
+
2327
+ AUTO-CAPTURE — OPT IN ONLY AFTER A PRIVACY AUDIT
2328
+ const stopAutoCapture = ductape.analytics.enableAutoCapture({
2329
+ session: () => authSession?.token,
2330
+ clicks: false,
2331
+ pageviews: true,
2332
+ maskTextSelectors: [
2333
+ '[data-private]',
2334
+ '[data-secret]',
2335
+ '[data-player-message]',
2336
+ '[data-intelligence-report]',
2337
+ ],
2338
+ });
2339
+ stopAutoCapture(); // or ductape.analytics.disableAutoCapture()
2340
+
2341
+ Mark sensitive UI with data-private/data-secret attributes. Text masking may not mask attributes,
2342
+ IDs, URLs, element names, custom properties, console errors, or network errors. Inspect actual
2343
+ payloads before production. For hidden-information products, begin with reviewed automatic
2344
+ pageviews, clicks disabled, and preferred custom named events.
2345
+
2346
+ VISITOR ID
2347
+ const visitorId = ductape.analytics.getVisitorId();
2348
+ A visitor ID is anonymous analytics identity, not authentication or authorization.
2349
+
2350
+ FRONTEND SESSION LIFECYCLE
2351
+ Before login:
2352
+ - Track anonymous pageviews/onboarding; do not invent a session.
2353
+ After login/registration:
2354
+ - Store the session securely, identify(fullSessionToken), track success, start authenticated
2355
+ pageviews, and pass the same token to user-context realtime/component operations.
2356
+ After refresh:
2357
+ - Replace the old token, identify(newSessionToken), update realtime connections/subscriptions,
2358
+ and use the refreshed token for future backend operations.
2359
+ Logout:
2360
+ - Optionally track logout_initiated, flush, revoke, disconnect realtime, clearSession, then
2361
+ remove local authentication.
2362
+ Refresh failure/revocation:
2363
+ - Disconnect user-context clients, clearSession, clear local auth, navigate to authentication,
2364
+ and track only anonymous events afterward.
2365
+
2366
+ REACT ROUTE TRACKING (verify installed exports first)
2367
+ import { useAnalytics, useDuctape } from '@ductape/react';
2368
+ import { useEffect } from 'react';
2369
+ import { useLocation } from 'react-router-dom';
2370
+
2371
+ function ProductAnalytics({ sessionToken }: { sessionToken?: string }) {
2372
+ const analytics = useAnalytics();
2373
+ const { client } = useDuctape();
2374
+ const location = useLocation();
2375
+ useEffect(() => {
2376
+ if (sessionToken) analytics.identify(sessionToken);
2377
+ else client.analytics.clearSession();
2378
+ }, [analytics, client, sessionToken]);
2379
+ useEffect(() => {
2380
+ void analytics.pageview({ path: location.pathname, title: document.title });
2381
+ }, [analytics, location.pathname]);
2382
+ return null;
2383
+ }
2384
+
2385
+ SAFE EVENT TAXONOMY
2386
+ Define stable names centrally; do not invent variants throughout components.
2387
+ Authentication:
2388
+ registration_started, registration_completed, registration_failed,
2389
+ login_started, login_completed, login_failed, session_refreshed,
2390
+ session_refresh_failed, logout_completed
2391
+ Match:
2392
+ match_list_viewed, match_creation_started, match_created, match_joined, lobby_viewed,
2393
+ player_marked_ready, match_preparation_started, world_loaded, match_reconnected,
2394
+ match_completed, endgame_viewed
2395
+ Orders:
2396
+ order_form_opened, order_previewed, order_submission_started, order_submitted,
2397
+ order_submission_failed, order_cancelled, boundary_result_viewed
2398
+ Realtime:
2399
+ realtime_connect_started, realtime_connected, realtime_disconnected,
2400
+ realtime_reconnect_attempted, realtime_subscription_failed, projection_refresh_failed,
2401
+ client_error
2402
+ Funnels:
2403
+ tutorial_started, tutorial_step_completed, tutorial_abandoned, first_match_created,
2404
+ first_order_submitted, first_boundary_viewed, first_match_completed
2405
+
2406
+ Safe properties include matchId, orderType, screen, tick, result, and errorCategory.
2407
+
2408
+ HIDDEN AND SENSITIVE DATA — NEVER SEND TO ANALYTICS
2409
+ Do not track exact hidden formations, operative/handler identities, secret operation payloads,
2410
+ false-report truth markers, undiscovered evasion details, invisible treaties, canonical hidden
2411
+ map state, private messages, passwords, authorization headers, session/refresh tokens in event
2412
+ properties, or full errors/records that may contain secrets. Analytics must observe usage, not
2413
+ become a hidden-state side channel.
2414
+
2415
+ CORRELATION
2416
+ Frontend: create traceId = crypto.randomUUID(), track intent with traceId, and send traceId with
2417
+ the actual request. Backend logs the same traceId and propagates session to Ductape operations.
2418
+ Track completion with the same traceId.
2419
+
2420
+ traceId = correlation
2421
+ session = actor attribution
2422
+ idempotencyKey = duplicate prevention
2423
+ matchId/orderId = domain identity
2424
+ These values are not interchangeable.
2425
+
2426
+ OWNERSHIP BOUNDARIES
2427
+ Analytics is never an authoritative order, authorization proof, or gameplay source of truth.
2428
+ Analytics failure must not block or alter gameplay. The server verifies sessions independently;
2429
+ authoritative database and Event streams remain the source of truth. Prefer non-blocking
2430
+ analytics except explicit best-effort flushes at safe lifecycle transitions.
2431
+
2432
+ FRONTEND PROJECT AUDIT
2433
+ Inspect installed @ductape/client/react/vue versions and actual exports; client/provider setup;
2434
+ identify after login and refresh; clearSession on logout/account switch; SPA pageviews; auto-
2435
+ capture and masking; taxonomy consistency; sensitive custom properties; and shared trace IDs.
2436
+ Report capability as Present/Missing/Partial/Not applicable with concrete findings.
2437
+
2438
+ WHEN “SESSION ACTIVITY IS MISSING FROM THE DASHBOARD”
2439
+ Investigate both tracks before blaming the SDK.
2440
+ Backend: start/verify format, correct env, session on database/Event/Feature/notification/etc.,
2441
+ and intentional background sessionlessness.
2442
+ Frontend: identify after login/refresh, clearSession on logout, pageviews/custom events, flush,
2443
+ correct publishable key/product/env, installed API compatibility, browser failures, and privacy
2444
+ controls that may suppress events.
2445
+
2446
+ PAYLOAD RECIPES
2447
+ Anonymous:
2448
+ await ductape.analytics.pageview({ path: window.location.pathname, title: document.title });
2449
+ Authenticated:
2450
+ ductape.analytics.identify(playerSessionToken);
2451
+ await ductape.analytics.track({
2452
+ event: 'match_created', session: playerSessionToken, traceId, properties: { matchId },
2453
+ });
2454
+ Logout:
2455
+ await ductape.analytics.flush();
2456
+ ductape.analytics.clearSession();
2225
2457
  `.trim(),
2226
2458
  features: `
2227
2459
  DUCTAPE FEATURES
@@ -2907,9 +3139,15 @@ SESSIONS SERVICE
2907
3139
  const { token } = await ductape.sessions.refresh({ refreshToken: '...' });
2908
3140
 
2909
3141
  ANALYTICS SERVICE
2910
- ductape.analytics.pageview({ page: '/dashboard' });
2911
- ductape.analytics.track('button_click', { button: 'sign-up' });
2912
- ductape.analytics.identify({ userId: 'u_123', traits: { plan: 'pro' } });
3142
+ ductape.analytics.identify('player-session:eyJ...'); // full Ductape session token
3143
+ await ductape.analytics.pageview({ path: '/dashboard', title: document.title });
3144
+ await ductape.analytics.track({
3145
+ event: 'button_clicked',
3146
+ properties: { button: 'sign-up' },
3147
+ });
3148
+ await ductape.analytics.flush();
3149
+ ductape.analytics.clearSession(); // logout/account switch
3150
+ See ductape_docs({ topic: "frontend-analytics" }) before enabling auto-capture.
2913
3151
 
2914
3152
  FRAMEWORK-SPECIFIC PACKAGES
2915
3153
  For React and Vue projects, use the dedicated packages instead of managing the client manually:
@@ -3010,13 +3248,15 @@ AGENT HOOKS
3010
3248
  useAgentSignal(hookOptions?) → { mutate, isLoading, error }
3011
3249
 
3012
3250
  BROKER HOOKS
3013
- useBroker(broker, options?)
3251
+ useBroker()
3014
3252
  → { isConnected, isConnecting, error, connect, disconnect }
3253
+ connect({ broker, session?, product?, env? }) forwards the complete options object to
3254
+ @ductape/client. Pass session for player-scoped authorization.
3015
3255
  useBrokerPublish(hookOptions?)
3016
3256
  → { mutate({ topic, message, headers?, key? }), isLoading, error, data }
3017
3257
  useBrokerSubscription(subscribeOptions, hookOptions?)
3018
3258
  → { data: BrokerMessage[], isSubscribed, error, unsubscribe, resubscribe }
3019
- subscribeOptions: { topic, group? }
3259
+ subscribeOptions: { broker?, topic, group?, session?, product?, env? }
3020
3260
 
3021
3261
  GRAPH HOOKS
3022
3262
  useGraph(graphTag) → { isConnected, isConnecting, error, connect, disconnect }
@@ -3086,7 +3326,10 @@ ACTIONS HOOKS
3086
3326
  useActionRun(hookOptions?) → { mutate, isLoading, error, data }
3087
3327
 
3088
3328
  ANALYTICS HOOK
3089
- useAnalytics() → { pageview, track, identify }
3329
+ useAnalytics() → { pageview, track, identify, visitorId, enableAutoCapture, flush }
3330
+ The currently installed hook does NOT return clearSession or disableAutoCapture.
3331
+ Use useDuctape().client.analytics.clearSession()/disableAutoCapture(), or verify a newer installed
3332
+ package export before generating those hook calls. See ductape_docs({ topic: "frontend-analytics" }).
3090
3333
 
3091
3334
  hookOptions pattern (applies to all hooks):
3092
3335
  enabled? boolean — false skips auto-fetch/subscribe
@@ -3255,7 +3498,10 @@ ACTIONS COMPOSABLES
3255
3498
  useActionRun(composableOptions?) → { mutate, isLoading, error, data }
3256
3499
 
3257
3500
  ANALYTICS COMPOSABLE
3258
- useAnalytics() → { pageview, track, identify }
3501
+ useAnalytics() → { pageview, track, identify, visitorId, enableAutoCapture, flush }
3502
+ The currently installed composable does NOT return clearSession or disableAutoCapture.
3503
+ Use useDuctape().client.analytics.clearSession()/disableAutoCapture(), or verify a newer installed
3504
+ package export before generating those composable calls. See ductape_docs({ topic: "frontend-analytics" }).
3259
3505
 
3260
3506
  composableOptions pattern (applies to all composables):
3261
3507
  enabled? boolean — false skips auto-fetch/subscribe
@@ -3598,7 +3844,7 @@ async function main() {
3598
3844
  'index strategy, operation types) that should be confirmed with the user first.\n\n' +
3599
3845
  'Available topics: transactions, presave, triggers, aggregations, migrations, indexes, performance, actions, ' +
3600
3846
  'graphs, storage, cloud, vector, warehouse, secrets, apps, products, sessions, caches, ' +
3601
- 'notifications, resilience, features, events, logs, frontend, client, react, vue',
3847
+ 'notifications, resilience, features, events, logs, frontend, frontend-analytics, client, react, vue',
3602
3848
  inputSchema: docsInputSchema,
3603
3849
  }, docsHandler);
3604
3850
  server.registerTool('ductape_cli', {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ductape/mcp",
3
- "version": "0.1.49",
3
+ "version": "0.1.50",
4
4
  "description": "MCP server that exposes Ductape SDK operations via the backend proxy",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -9,6 +9,7 @@
9
9
  },
10
10
  "scripts": {
11
11
  "build": "tsc",
12
+ "test": "node scripts/check-frontend-analytics-guidance.mjs",
12
13
  "start": "node dist/index.js",
13
14
  "dev": "tsx src/index.ts"
14
15
  },
@@ -0,0 +1,37 @@
1
+ import assert from 'node:assert/strict';
2
+ import { readFileSync } from 'node:fs';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { dirname, resolve } from 'node:path';
5
+
6
+ const here = dirname(fileURLToPath(import.meta.url));
7
+ const source = readFileSync(resolve(here, '../src/index.ts'), 'utf8');
8
+
9
+ const checks = [
10
+ ['dedicated docs topic', /'frontend-analytics': `/],
11
+ ['anonymous analytics without an invented session', /Before login:[\s\S]*do not invent a session/i],
12
+ ['identify after authentication', /After login\/registration:[\s\S]*identify\(fullSessionToken\)/i],
13
+ ['identify after refresh', /After refresh:[\s\S]*identify\(newSessionToken\)/i],
14
+ ['clear identity on logout and account switching', /LOGOUT \/ ACCOUNT SWITCHING[\s\S]*clearSession\(\)/],
15
+ ['SPA route pageviews', /location\.pathname[\s\S]*analytics\.pageview/],
16
+ ['pageviews and custom events', /CUSTOM EVENTS[\s\S]*PAGE VIEWS/],
17
+ ['analytics is non-authoritative', /Analytics is never an authoritative order/],
18
+ ['tokens excluded from properties', /session\/refresh tokens in event[\s\S]*properties/],
19
+ ['hidden multiplayer state warning', /HIDDEN AND SENSITIVE DATA/],
20
+ ['masking review before auto-capture', /AUTO-CAPTURE — OPT IN ONLY AFTER A PRIVACY AUDIT/],
21
+ ['visitor ID is not authentication', /visitor ID is anonymous analytics identity, not authentication/i],
22
+ ['trace correlation', /traceId\s*= correlation/],
23
+ ['dashboard troubleshooting covers both tracks', /Investigate both tracks before blaming the SDK/],
24
+ ['installed exports take precedence', /Installed package types and exports determine what can be called now/],
25
+ ];
26
+
27
+ for (const [name, pattern] of checks) {
28
+ assert.match(source, pattern, `Missing frontend analytics guidance: ${name}`);
29
+ }
30
+
31
+ assert.doesNotMatch(
32
+ source,
33
+ /analytics\.identify\(\{\s*userId/,
34
+ 'MCP guidance must not use the obsolete identify({ userId, traits }) signature',
35
+ );
36
+
37
+ console.log(`frontend analytics guidance: ${checks.length + 1} acceptance checks passed`);
package/src/index.ts CHANGED
@@ -1242,7 +1242,7 @@ const docsInputSchema = z.object({
1242
1242
  'Feature topic to look up. Supported: ' +
1243
1243
  'transactions, presave, triggers, aggregations, migrations, indexes, performance, actions, ' +
1244
1244
  'graphs, storage, cloud, vector, warehouse, secrets, apps, products, sessions, caches, ' +
1245
- 'notifications, resilience, features, events, logs, frontend, client, react, vue',
1245
+ 'notifications, resilience, features, events, logs, frontend, frontend-analytics, client, react, vue',
1246
1246
  ),
1247
1247
  });
1248
1248
 
@@ -1280,6 +1280,11 @@ SHARED APPLICATION LIFECYCLE
1280
1280
  connectionState to render disconnected/reconnecting UI; do not create duplicate subscriptions.
1281
1281
  7. Disconnect resource sessions and the root client when the owning application scope is torn down.
1282
1282
 
1283
+ PRODUCT ANALYTICS
1284
+ Frontend product analytics complements backend session propagation; neither replaces the other.
1285
+ Continue with ductape_docs({ topic: "frontend-analytics" }) for identity lifecycle, pageviews,
1286
+ custom events, privacy masking, hidden-state safety, trace correlation, and package-version checks.
1287
+
1283
1288
  AUTHENTICATION AND SECURITY
1284
1289
  - Use publishableKey in browser applications. Never ship workspace private keys or privileged
1285
1290
  access keys in frontend bundles.
@@ -2041,6 +2046,7 @@ Resource registration (all via ductape_cli or ductape_execute — see per-topic
2041
2046
  caches → ductape_docs({ topic: "caches" })
2042
2047
  notifications → ductape_docs({ topic: "notifications" })
2043
2048
  sessions → ductape_docs({ topic: "sessions" })
2049
+ frontend analytics → ductape_docs({ topic: "frontend-analytics" })
2044
2050
  resilience → ductape_docs({ topic: "resilience" })
2045
2051
  features → ductape_docs({ topic: "features" })
2046
2052
 
@@ -2129,6 +2135,16 @@ Analytics:
2129
2135
 
2130
2136
  Token format: "session_tag:jwt_token" — pass this format verbatim wherever session is expected.
2131
2137
  JWT is signed with the product private key (not a symmetric shared secret).
2138
+
2139
+ SESSION PROPAGATION
2140
+ Preserve the original full token on every user-context database, Event, Feature, notification,
2141
+ storage, graph, vector, action, and other runtime operation that accepts session. Do not replace
2142
+ it with the session tag, user ID, decoded claims, or refresh token. Durable user-initiated work
2143
+ should retain the initiating token; intentional system/background work should be sessionless.
2144
+
2145
+ Backend propagation attributes component activity to an actor, but it does not record frontend
2146
+ pageviews, navigation, UI intent, funnels, or client failures. Continue immediately with
2147
+ ductape_docs({ topic: "frontend-analytics" }); both layers are required for a complete picture.
2132
2148
  `.trim(),
2133
2149
 
2134
2150
  caches: `
@@ -2296,6 +2312,223 @@ Input template references: $Input{field} → maps declared input to the probe's
2296
2312
  Provider status: available | unavailable
2297
2313
  `.trim(),
2298
2314
 
2315
+ 'frontend-analytics': `
2316
+ DUCTAPE FRONTEND PRODUCT ANALYTICS AND SESSION ACTIVITY
2317
+
2318
+ Frontend analytics and backend session attribution are complementary; neither replaces the other.
2319
+
2320
+ BACKEND OPERATION ATTRIBUTION
2321
+ Pass the original full Ductape session token to every user-initiated database, Event, Feature,
2322
+ notification, storage, graph, vector, action, and other runtime operation that accepts session.
2323
+ This attributes server work to the authenticated actor. Durable work should retain the initiating
2324
+ session; intentional system/background work should remain sessionless.
2325
+
2326
+ FRONTEND PRODUCT ANALYTICS
2327
+ Use the browser Analytics service for anonymous visits, authenticated page views, navigation,
2328
+ UI interactions, funnels, client errors, realtime state, and product-surface engagement.
2329
+
2330
+ IMPORTANT:
2331
+ Passing session to backend component calls does not replace frontend analytics.
2332
+ Calling analytics.identify(sessionToken) and tracking frontend events does not replace backend
2333
+ session propagation. A complete activity picture requires both.
2334
+
2335
+ VERSION AND DOCUMENTATION PRECEDENCE
2336
+ 1. Installed package types and exports determine what can be called now.
2337
+ 2. Version-matched package documentation explains intended usage.
2338
+ 3. Current docs/docs/Frontend describes the latest supported design.
2339
+ 4. If they disagree, report the mismatch; never fabricate compatibility.
2340
+
2341
+ Inspect package.json plus package exports/type declarations before recommending framework hooks.
2342
+ The current repository exports useAnalytics from @ductape/react and @ductape/vue. Their installed
2343
+ hook/composable exposes track, pageview, identify, visitorId, enableAutoCapture, and flush.
2344
+ clearSession and disableAutoCapture are available on client.analytics but are not currently
2345
+ returned by those framework wrappers. Use useDuctape().client.analytics for those calls, or
2346
+ recommend a package version that exports them; do not generate a hook method that does not exist.
2347
+
2348
+ CLIENT API — IDENTIFY AFTER AUTHENTICATION
2349
+ ductape.analytics.identify(sessionToken);
2350
+
2351
+ sessionToken is the complete value returned by Ductape, in "player-session:jwt" format.
2352
+ It is NOT a player ID, session tag, session ID, decoded claims, or refresh token.
2353
+ identify links subsequent frontend analytics to the authenticated Ductape session and lets
2354
+ supported analytics correlate anonymous pre-login activity with authenticated activity.
2355
+
2356
+ LOGOUT / ACCOUNT SWITCHING
2357
+ await ductape.analytics.flush();
2358
+ ductape.analytics.clearSession();
2359
+
2360
+ Clear analytics identity when logout or revocation succeeds, refresh fails irrecoverably, local
2361
+ authentication is removed, or a different user is about to authenticate in the same browser.
2362
+ Removing only the application token can leave later anonymous/next-user events associated with
2363
+ the previous analytics identity. Flush is best-effort; browser shutdown does not guarantee it.
2364
+
2365
+ CUSTOM EVENTS
2366
+ await ductape.analytics.track({
2367
+ event: 'order_submitted',
2368
+ traceId,
2369
+ properties: { matchId, orderType },
2370
+ });
2371
+
2372
+ IAnalyticsTrackOptions:
2373
+ event: string
2374
+ properties?: Record<string, unknown>
2375
+ session?: string
2376
+ product?: string
2377
+ env?: string
2378
+ traceId?: string
2379
+ context?: { url?, path?, referrer?, locale?, screen?: { width, height } }
2380
+
2381
+ identify establishes the default analytics session. An individual event may explicitly provide
2382
+ session. Normally use product/env from client configuration. traceId correlates frontend intent
2383
+ with backend logs, Events, Features, and order processing.
2384
+
2385
+ PAGE VIEWS
2386
+ await ductape.analytics.pageview({
2387
+ path: location.pathname,
2388
+ title: document.title,
2389
+ properties: { matchId, screen: 'governance' },
2390
+ });
2391
+
2392
+ IAnalyticsPageviewOptions:
2393
+ path?: string
2394
+ title?: string
2395
+ session?: string
2396
+ product?: string
2397
+ env?: string
2398
+ properties?: Record<string, unknown>
2399
+
2400
+ AUTO-CAPTURE — OPT IN ONLY AFTER A PRIVACY AUDIT
2401
+ const stopAutoCapture = ductape.analytics.enableAutoCapture({
2402
+ session: () => authSession?.token,
2403
+ clicks: false,
2404
+ pageviews: true,
2405
+ maskTextSelectors: [
2406
+ '[data-private]',
2407
+ '[data-secret]',
2408
+ '[data-player-message]',
2409
+ '[data-intelligence-report]',
2410
+ ],
2411
+ });
2412
+ stopAutoCapture(); // or ductape.analytics.disableAutoCapture()
2413
+
2414
+ Mark sensitive UI with data-private/data-secret attributes. Text masking may not mask attributes,
2415
+ IDs, URLs, element names, custom properties, console errors, or network errors. Inspect actual
2416
+ payloads before production. For hidden-information products, begin with reviewed automatic
2417
+ pageviews, clicks disabled, and preferred custom named events.
2418
+
2419
+ VISITOR ID
2420
+ const visitorId = ductape.analytics.getVisitorId();
2421
+ A visitor ID is anonymous analytics identity, not authentication or authorization.
2422
+
2423
+ FRONTEND SESSION LIFECYCLE
2424
+ Before login:
2425
+ - Track anonymous pageviews/onboarding; do not invent a session.
2426
+ After login/registration:
2427
+ - Store the session securely, identify(fullSessionToken), track success, start authenticated
2428
+ pageviews, and pass the same token to user-context realtime/component operations.
2429
+ After refresh:
2430
+ - Replace the old token, identify(newSessionToken), update realtime connections/subscriptions,
2431
+ and use the refreshed token for future backend operations.
2432
+ Logout:
2433
+ - Optionally track logout_initiated, flush, revoke, disconnect realtime, clearSession, then
2434
+ remove local authentication.
2435
+ Refresh failure/revocation:
2436
+ - Disconnect user-context clients, clearSession, clear local auth, navigate to authentication,
2437
+ and track only anonymous events afterward.
2438
+
2439
+ REACT ROUTE TRACKING (verify installed exports first)
2440
+ import { useAnalytics, useDuctape } from '@ductape/react';
2441
+ import { useEffect } from 'react';
2442
+ import { useLocation } from 'react-router-dom';
2443
+
2444
+ function ProductAnalytics({ sessionToken }: { sessionToken?: string }) {
2445
+ const analytics = useAnalytics();
2446
+ const { client } = useDuctape();
2447
+ const location = useLocation();
2448
+ useEffect(() => {
2449
+ if (sessionToken) analytics.identify(sessionToken);
2450
+ else client.analytics.clearSession();
2451
+ }, [analytics, client, sessionToken]);
2452
+ useEffect(() => {
2453
+ void analytics.pageview({ path: location.pathname, title: document.title });
2454
+ }, [analytics, location.pathname]);
2455
+ return null;
2456
+ }
2457
+
2458
+ SAFE EVENT TAXONOMY
2459
+ Define stable names centrally; do not invent variants throughout components.
2460
+ Authentication:
2461
+ registration_started, registration_completed, registration_failed,
2462
+ login_started, login_completed, login_failed, session_refreshed,
2463
+ session_refresh_failed, logout_completed
2464
+ Match:
2465
+ match_list_viewed, match_creation_started, match_created, match_joined, lobby_viewed,
2466
+ player_marked_ready, match_preparation_started, world_loaded, match_reconnected,
2467
+ match_completed, endgame_viewed
2468
+ Orders:
2469
+ order_form_opened, order_previewed, order_submission_started, order_submitted,
2470
+ order_submission_failed, order_cancelled, boundary_result_viewed
2471
+ Realtime:
2472
+ realtime_connect_started, realtime_connected, realtime_disconnected,
2473
+ realtime_reconnect_attempted, realtime_subscription_failed, projection_refresh_failed,
2474
+ client_error
2475
+ Funnels:
2476
+ tutorial_started, tutorial_step_completed, tutorial_abandoned, first_match_created,
2477
+ first_order_submitted, first_boundary_viewed, first_match_completed
2478
+
2479
+ Safe properties include matchId, orderType, screen, tick, result, and errorCategory.
2480
+
2481
+ HIDDEN AND SENSITIVE DATA — NEVER SEND TO ANALYTICS
2482
+ Do not track exact hidden formations, operative/handler identities, secret operation payloads,
2483
+ false-report truth markers, undiscovered evasion details, invisible treaties, canonical hidden
2484
+ map state, private messages, passwords, authorization headers, session/refresh tokens in event
2485
+ properties, or full errors/records that may contain secrets. Analytics must observe usage, not
2486
+ become a hidden-state side channel.
2487
+
2488
+ CORRELATION
2489
+ Frontend: create traceId = crypto.randomUUID(), track intent with traceId, and send traceId with
2490
+ the actual request. Backend logs the same traceId and propagates session to Ductape operations.
2491
+ Track completion with the same traceId.
2492
+
2493
+ traceId = correlation
2494
+ session = actor attribution
2495
+ idempotencyKey = duplicate prevention
2496
+ matchId/orderId = domain identity
2497
+ These values are not interchangeable.
2498
+
2499
+ OWNERSHIP BOUNDARIES
2500
+ Analytics is never an authoritative order, authorization proof, or gameplay source of truth.
2501
+ Analytics failure must not block or alter gameplay. The server verifies sessions independently;
2502
+ authoritative database and Event streams remain the source of truth. Prefer non-blocking
2503
+ analytics except explicit best-effort flushes at safe lifecycle transitions.
2504
+
2505
+ FRONTEND PROJECT AUDIT
2506
+ Inspect installed @ductape/client/react/vue versions and actual exports; client/provider setup;
2507
+ identify after login and refresh; clearSession on logout/account switch; SPA pageviews; auto-
2508
+ capture and masking; taxonomy consistency; sensitive custom properties; and shared trace IDs.
2509
+ Report capability as Present/Missing/Partial/Not applicable with concrete findings.
2510
+
2511
+ WHEN “SESSION ACTIVITY IS MISSING FROM THE DASHBOARD”
2512
+ Investigate both tracks before blaming the SDK.
2513
+ Backend: start/verify format, correct env, session on database/Event/Feature/notification/etc.,
2514
+ and intentional background sessionlessness.
2515
+ Frontend: identify after login/refresh, clearSession on logout, pageviews/custom events, flush,
2516
+ correct publishable key/product/env, installed API compatibility, browser failures, and privacy
2517
+ controls that may suppress events.
2518
+
2519
+ PAYLOAD RECIPES
2520
+ Anonymous:
2521
+ await ductape.analytics.pageview({ path: window.location.pathname, title: document.title });
2522
+ Authenticated:
2523
+ ductape.analytics.identify(playerSessionToken);
2524
+ await ductape.analytics.track({
2525
+ event: 'match_created', session: playerSessionToken, traceId, properties: { matchId },
2526
+ });
2527
+ Logout:
2528
+ await ductape.analytics.flush();
2529
+ ductape.analytics.clearSession();
2530
+ `.trim(),
2531
+
2299
2532
  features: `
2300
2533
  DUCTAPE FEATURES
2301
2534
 
@@ -2983,9 +3216,15 @@ SESSIONS SERVICE
2983
3216
  const { token } = await ductape.sessions.refresh({ refreshToken: '...' });
2984
3217
 
2985
3218
  ANALYTICS SERVICE
2986
- ductape.analytics.pageview({ page: '/dashboard' });
2987
- ductape.analytics.track('button_click', { button: 'sign-up' });
2988
- ductape.analytics.identify({ userId: 'u_123', traits: { plan: 'pro' } });
3219
+ ductape.analytics.identify('player-session:eyJ...'); // full Ductape session token
3220
+ await ductape.analytics.pageview({ path: '/dashboard', title: document.title });
3221
+ await ductape.analytics.track({
3222
+ event: 'button_clicked',
3223
+ properties: { button: 'sign-up' },
3224
+ });
3225
+ await ductape.analytics.flush();
3226
+ ductape.analytics.clearSession(); // logout/account switch
3227
+ See ductape_docs({ topic: "frontend-analytics" }) before enabling auto-capture.
2989
3228
 
2990
3229
  FRAMEWORK-SPECIFIC PACKAGES
2991
3230
  For React and Vue projects, use the dedicated packages instead of managing the client manually:
@@ -3087,13 +3326,15 @@ AGENT HOOKS
3087
3326
  useAgentSignal(hookOptions?) → { mutate, isLoading, error }
3088
3327
 
3089
3328
  BROKER HOOKS
3090
- useBroker(broker, options?)
3329
+ useBroker()
3091
3330
  → { isConnected, isConnecting, error, connect, disconnect }
3331
+ connect({ broker, session?, product?, env? }) forwards the complete options object to
3332
+ @ductape/client. Pass session for player-scoped authorization.
3092
3333
  useBrokerPublish(hookOptions?)
3093
3334
  → { mutate({ topic, message, headers?, key? }), isLoading, error, data }
3094
3335
  useBrokerSubscription(subscribeOptions, hookOptions?)
3095
3336
  → { data: BrokerMessage[], isSubscribed, error, unsubscribe, resubscribe }
3096
- subscribeOptions: { topic, group? }
3337
+ subscribeOptions: { broker?, topic, group?, session?, product?, env? }
3097
3338
 
3098
3339
  GRAPH HOOKS
3099
3340
  useGraph(graphTag) → { isConnected, isConnecting, error, connect, disconnect }
@@ -3163,7 +3404,10 @@ ACTIONS HOOKS
3163
3404
  useActionRun(hookOptions?) → { mutate, isLoading, error, data }
3164
3405
 
3165
3406
  ANALYTICS HOOK
3166
- useAnalytics() → { pageview, track, identify }
3407
+ useAnalytics() → { pageview, track, identify, visitorId, enableAutoCapture, flush }
3408
+ The currently installed hook does NOT return clearSession or disableAutoCapture.
3409
+ Use useDuctape().client.analytics.clearSession()/disableAutoCapture(), or verify a newer installed
3410
+ package export before generating those hook calls. See ductape_docs({ topic: "frontend-analytics" }).
3167
3411
 
3168
3412
  hookOptions pattern (applies to all hooks):
3169
3413
  enabled? boolean — false skips auto-fetch/subscribe
@@ -3333,7 +3577,10 @@ ACTIONS COMPOSABLES
3333
3577
  useActionRun(composableOptions?) → { mutate, isLoading, error, data }
3334
3578
 
3335
3579
  ANALYTICS COMPOSABLE
3336
- useAnalytics() → { pageview, track, identify }
3580
+ useAnalytics() → { pageview, track, identify, visitorId, enableAutoCapture, flush }
3581
+ The currently installed composable does NOT return clearSession or disableAutoCapture.
3582
+ Use useDuctape().client.analytics.clearSession()/disableAutoCapture(), or verify a newer installed
3583
+ package export before generating those composable calls. See ductape_docs({ topic: "frontend-analytics" }).
3337
3584
 
3338
3585
  composableOptions pattern (applies to all composables):
3339
3586
  enabled? boolean — false skips auto-fetch/subscribe
@@ -3732,7 +3979,7 @@ async function main() {
3732
3979
  'index strategy, operation types) that should be confirmed with the user first.\n\n' +
3733
3980
  'Available topics: transactions, presave, triggers, aggregations, migrations, indexes, performance, actions, ' +
3734
3981
  'graphs, storage, cloud, vector, warehouse, secrets, apps, products, sessions, caches, ' +
3735
- 'notifications, resilience, features, events, logs, frontend, client, react, vue',
3982
+ 'notifications, resilience, features, events, logs, frontend, frontend-analytics, client, react, vue',
3736
3983
  inputSchema: docsInputSchema,
3737
3984
  },
3738
3985
  docsHandler,