@ductape/mcp 0.1.48 → 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 +304 -9
- package/package.json +2 -1
- package/scripts/check-frontend-analytics-guidance.mjs +37 -0
- package/src/index.ts +306 -9
package/dist/index.js
CHANGED
|
@@ -1192,9 +1192,63 @@ 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'),
|
|
1195
|
+
'notifications, resilience, features, events, logs, frontend, frontend-analytics, client, react, vue'),
|
|
1196
1196
|
});
|
|
1197
1197
|
const DOCS = {
|
|
1198
|
+
frontend: `
|
|
1199
|
+
DUCTAPE FRONTEND SDK GUIDE
|
|
1200
|
+
|
|
1201
|
+
Choose one integration package:
|
|
1202
|
+
React 17+ — npm install @ductape/react
|
|
1203
|
+
Provider and hooks built on @ductape/client.
|
|
1204
|
+
Continue with ductape_docs({ topic: "react" }).
|
|
1205
|
+
Vue 3+ — npm install @ductape/vue
|
|
1206
|
+
Plugin and composables built on @ductape/client.
|
|
1207
|
+
Continue with ductape_docs({ topic: "vue" }).
|
|
1208
|
+
Other UI — npm install @ductape/client
|
|
1209
|
+
frameworks Use directly with Svelte, Angular, vanilla JavaScript, or a custom adapter.
|
|
1210
|
+
Continue with ductape_docs({ topic: "client" }).
|
|
1211
|
+
|
|
1212
|
+
Do not install @ductape/client separately when using @ductape/react or @ductape/vue unless the
|
|
1213
|
+
application also needs direct access to a client API not exposed by the framework package. Both
|
|
1214
|
+
framework packages wrap @ductape/client and expose the underlying client through useDuctape().
|
|
1215
|
+
|
|
1216
|
+
SHARED APPLICATION LIFECYCLE
|
|
1217
|
+
1. Create one client at the application root using a browser-safe publishableKey, proxy baseUrl,
|
|
1218
|
+
default product tag, and environment slug.
|
|
1219
|
+
2. Connect the real-time client only in the browser. In React use DuctapeProvider autoConnect or
|
|
1220
|
+
connect() after mount; in Vue use createDuctape({ autoConnect: true }) or connect() in onMounted.
|
|
1221
|
+
3. Authenticate the player with the sessions API and retain the returned session token according
|
|
1222
|
+
to the application's security policy. Refresh it before expiry (the React/Vue packages provide
|
|
1223
|
+
useSessionAutoRefresh) and revoke it on logout.
|
|
1224
|
+
4. Pass the session value when connecting a broker that requires player-scoped authorization.
|
|
1225
|
+
5. Subscribe through the framework hook/composable or the client service. Always unsubscribe on
|
|
1226
|
+
component teardown; the framework integrations do this automatically for declarative hooks.
|
|
1227
|
+
6. The underlying client reconnects its WebSocket and restores active subscriptions. Observe
|
|
1228
|
+
connectionState to render disconnected/reconnecting UI; do not create duplicate subscriptions.
|
|
1229
|
+
7. Disconnect resource sessions and the root client when the owning application scope is torn down.
|
|
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
|
+
|
|
1236
|
+
AUTHENTICATION AND SECURITY
|
|
1237
|
+
- Use publishableKey in browser applications. Never ship workspace private keys or privileged
|
|
1238
|
+
access keys in frontend bundles.
|
|
1239
|
+
- A Ductape player session and the real-time transport connection are separate: authenticate or
|
|
1240
|
+
refresh the session, then use that session when opening player-scoped broker resources.
|
|
1241
|
+
- For SSR, create/connect the real-time client only on the browser side.
|
|
1242
|
+
|
|
1243
|
+
MIGRATING A CUSTOM WRAPPER
|
|
1244
|
+
- Keep @ductape/client when the wrapper implements application-specific projection or state logic.
|
|
1245
|
+
- For React, move root connection ownership to DuctapeProvider, replace imperative subscriptions
|
|
1246
|
+
with useBroker/useBrokerSubscription, and keep projection reduction in an application hook.
|
|
1247
|
+
- For Vue, move root ownership to createDuctape(), replace imperative subscriptions with
|
|
1248
|
+
useBroker/useBrokerSubscription, and keep projection reduction in an application composable.
|
|
1249
|
+
- Verify session handoff, initial loading state, error state, reconnect UI, subscription cleanup,
|
|
1250
|
+
and duplicate-event behavior with a live end-to-end run before removing the old wrapper.
|
|
1251
|
+
`.trim(),
|
|
1198
1252
|
transactions: `
|
|
1199
1253
|
DUCTAPE DATABASE TRANSACTIONS
|
|
1200
1254
|
|
|
@@ -1924,6 +1978,7 @@ Resource registration (all via ductape_cli or ductape_execute — see per-topic
|
|
|
1924
1978
|
caches → ductape_docs({ topic: "caches" })
|
|
1925
1979
|
notifications → ductape_docs({ topic: "notifications" })
|
|
1926
1980
|
sessions → ductape_docs({ topic: "sessions" })
|
|
1981
|
+
frontend analytics → ductape_docs({ topic: "frontend-analytics" })
|
|
1927
1982
|
resilience → ductape_docs({ topic: "resilience" })
|
|
1928
1983
|
features → ductape_docs({ topic: "features" })
|
|
1929
1984
|
|
|
@@ -2011,6 +2066,16 @@ Analytics:
|
|
|
2011
2066
|
|
|
2012
2067
|
Token format: "session_tag:jwt_token" — pass this format verbatim wherever session is expected.
|
|
2013
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.
|
|
2014
2079
|
`.trim(),
|
|
2015
2080
|
caches: `
|
|
2016
2081
|
DUCTAPE CACHES
|
|
@@ -2173,6 +2238,222 @@ Failure actions: notification channels, HTTP webhooks, and/or message broker emi
|
|
|
2173
2238
|
be configured simultaneously on the same healthcheck.
|
|
2174
2239
|
Input template references: $Input{field} → maps declared input to the probe's action input.
|
|
2175
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();
|
|
2176
2457
|
`.trim(),
|
|
2177
2458
|
features: `
|
|
2178
2459
|
DUCTAPE FEATURES
|
|
@@ -2858,9 +3139,15 @@ SESSIONS SERVICE
|
|
|
2858
3139
|
const { token } = await ductape.sessions.refresh({ refreshToken: '...' });
|
|
2859
3140
|
|
|
2860
3141
|
ANALYTICS SERVICE
|
|
2861
|
-
ductape.analytics.
|
|
2862
|
-
ductape.analytics.
|
|
2863
|
-
ductape.analytics.
|
|
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.
|
|
2864
3151
|
|
|
2865
3152
|
FRAMEWORK-SPECIFIC PACKAGES
|
|
2866
3153
|
For React and Vue projects, use the dedicated packages instead of managing the client manually:
|
|
@@ -2961,13 +3248,15 @@ AGENT HOOKS
|
|
|
2961
3248
|
useAgentSignal(hookOptions?) → { mutate, isLoading, error }
|
|
2962
3249
|
|
|
2963
3250
|
BROKER HOOKS
|
|
2964
|
-
useBroker(
|
|
3251
|
+
useBroker()
|
|
2965
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.
|
|
2966
3255
|
useBrokerPublish(hookOptions?)
|
|
2967
3256
|
→ { mutate({ topic, message, headers?, key? }), isLoading, error, data }
|
|
2968
3257
|
useBrokerSubscription(subscribeOptions, hookOptions?)
|
|
2969
3258
|
→ { data: BrokerMessage[], isSubscribed, error, unsubscribe, resubscribe }
|
|
2970
|
-
subscribeOptions: { topic, group? }
|
|
3259
|
+
subscribeOptions: { broker?, topic, group?, session?, product?, env? }
|
|
2971
3260
|
|
|
2972
3261
|
GRAPH HOOKS
|
|
2973
3262
|
useGraph(graphTag) → { isConnected, isConnecting, error, connect, disconnect }
|
|
@@ -3037,7 +3326,10 @@ ACTIONS HOOKS
|
|
|
3037
3326
|
useActionRun(hookOptions?) → { mutate, isLoading, error, data }
|
|
3038
3327
|
|
|
3039
3328
|
ANALYTICS HOOK
|
|
3040
|
-
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" }).
|
|
3041
3333
|
|
|
3042
3334
|
hookOptions pattern (applies to all hooks):
|
|
3043
3335
|
enabled? boolean — false skips auto-fetch/subscribe
|
|
@@ -3206,7 +3498,10 @@ ACTIONS COMPOSABLES
|
|
|
3206
3498
|
useActionRun(composableOptions?) → { mutate, isLoading, error, data }
|
|
3207
3499
|
|
|
3208
3500
|
ANALYTICS COMPOSABLE
|
|
3209
|
-
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" }).
|
|
3210
3505
|
|
|
3211
3506
|
composableOptions pattern (applies to all composables):
|
|
3212
3507
|
enabled? boolean — false skips auto-fetch/subscribe
|
|
@@ -3549,7 +3844,7 @@ async function main() {
|
|
|
3549
3844
|
'index strategy, operation types) that should be confirmed with the user first.\n\n' +
|
|
3550
3845
|
'Available topics: transactions, presave, triggers, aggregations, migrations, indexes, performance, actions, ' +
|
|
3551
3846
|
'graphs, storage, cloud, vector, warehouse, secrets, apps, products, sessions, caches, ' +
|
|
3552
|
-
'notifications, resilience, features, events, logs, client, react, vue',
|
|
3847
|
+
'notifications, resilience, features, events, logs, frontend, frontend-analytics, client, react, vue',
|
|
3553
3848
|
inputSchema: docsInputSchema,
|
|
3554
3849
|
}, docsHandler);
|
|
3555
3850
|
server.registerTool('ductape_cli', {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ductape/mcp",
|
|
3
|
-
"version": "0.1.
|
|
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,11 +1242,66 @@ 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',
|
|
1245
|
+
'notifications, resilience, features, events, logs, frontend, frontend-analytics, client, react, vue',
|
|
1246
1246
|
),
|
|
1247
1247
|
});
|
|
1248
1248
|
|
|
1249
1249
|
const DOCS: Record<string, string> = {
|
|
1250
|
+
frontend: `
|
|
1251
|
+
DUCTAPE FRONTEND SDK GUIDE
|
|
1252
|
+
|
|
1253
|
+
Choose one integration package:
|
|
1254
|
+
React 17+ — npm install @ductape/react
|
|
1255
|
+
Provider and hooks built on @ductape/client.
|
|
1256
|
+
Continue with ductape_docs({ topic: "react" }).
|
|
1257
|
+
Vue 3+ — npm install @ductape/vue
|
|
1258
|
+
Plugin and composables built on @ductape/client.
|
|
1259
|
+
Continue with ductape_docs({ topic: "vue" }).
|
|
1260
|
+
Other UI — npm install @ductape/client
|
|
1261
|
+
frameworks Use directly with Svelte, Angular, vanilla JavaScript, or a custom adapter.
|
|
1262
|
+
Continue with ductape_docs({ topic: "client" }).
|
|
1263
|
+
|
|
1264
|
+
Do not install @ductape/client separately when using @ductape/react or @ductape/vue unless the
|
|
1265
|
+
application also needs direct access to a client API not exposed by the framework package. Both
|
|
1266
|
+
framework packages wrap @ductape/client and expose the underlying client through useDuctape().
|
|
1267
|
+
|
|
1268
|
+
SHARED APPLICATION LIFECYCLE
|
|
1269
|
+
1. Create one client at the application root using a browser-safe publishableKey, proxy baseUrl,
|
|
1270
|
+
default product tag, and environment slug.
|
|
1271
|
+
2. Connect the real-time client only in the browser. In React use DuctapeProvider autoConnect or
|
|
1272
|
+
connect() after mount; in Vue use createDuctape({ autoConnect: true }) or connect() in onMounted.
|
|
1273
|
+
3. Authenticate the player with the sessions API and retain the returned session token according
|
|
1274
|
+
to the application's security policy. Refresh it before expiry (the React/Vue packages provide
|
|
1275
|
+
useSessionAutoRefresh) and revoke it on logout.
|
|
1276
|
+
4. Pass the session value when connecting a broker that requires player-scoped authorization.
|
|
1277
|
+
5. Subscribe through the framework hook/composable or the client service. Always unsubscribe on
|
|
1278
|
+
component teardown; the framework integrations do this automatically for declarative hooks.
|
|
1279
|
+
6. The underlying client reconnects its WebSocket and restores active subscriptions. Observe
|
|
1280
|
+
connectionState to render disconnected/reconnecting UI; do not create duplicate subscriptions.
|
|
1281
|
+
7. Disconnect resource sessions and the root client when the owning application scope is torn down.
|
|
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
|
+
|
|
1288
|
+
AUTHENTICATION AND SECURITY
|
|
1289
|
+
- Use publishableKey in browser applications. Never ship workspace private keys or privileged
|
|
1290
|
+
access keys in frontend bundles.
|
|
1291
|
+
- A Ductape player session and the real-time transport connection are separate: authenticate or
|
|
1292
|
+
refresh the session, then use that session when opening player-scoped broker resources.
|
|
1293
|
+
- For SSR, create/connect the real-time client only on the browser side.
|
|
1294
|
+
|
|
1295
|
+
MIGRATING A CUSTOM WRAPPER
|
|
1296
|
+
- Keep @ductape/client when the wrapper implements application-specific projection or state logic.
|
|
1297
|
+
- For React, move root connection ownership to DuctapeProvider, replace imperative subscriptions
|
|
1298
|
+
with useBroker/useBrokerSubscription, and keep projection reduction in an application hook.
|
|
1299
|
+
- For Vue, move root ownership to createDuctape(), replace imperative subscriptions with
|
|
1300
|
+
useBroker/useBrokerSubscription, and keep projection reduction in an application composable.
|
|
1301
|
+
- Verify session handoff, initial loading state, error state, reconnect UI, subscription cleanup,
|
|
1302
|
+
and duplicate-event behavior with a live end-to-end run before removing the old wrapper.
|
|
1303
|
+
`.trim(),
|
|
1304
|
+
|
|
1250
1305
|
transactions: `
|
|
1251
1306
|
DUCTAPE DATABASE TRANSACTIONS
|
|
1252
1307
|
|
|
@@ -1991,6 +2046,7 @@ Resource registration (all via ductape_cli or ductape_execute — see per-topic
|
|
|
1991
2046
|
caches → ductape_docs({ topic: "caches" })
|
|
1992
2047
|
notifications → ductape_docs({ topic: "notifications" })
|
|
1993
2048
|
sessions → ductape_docs({ topic: "sessions" })
|
|
2049
|
+
frontend analytics → ductape_docs({ topic: "frontend-analytics" })
|
|
1994
2050
|
resilience → ductape_docs({ topic: "resilience" })
|
|
1995
2051
|
features → ductape_docs({ topic: "features" })
|
|
1996
2052
|
|
|
@@ -2079,6 +2135,16 @@ Analytics:
|
|
|
2079
2135
|
|
|
2080
2136
|
Token format: "session_tag:jwt_token" — pass this format verbatim wherever session is expected.
|
|
2081
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.
|
|
2082
2148
|
`.trim(),
|
|
2083
2149
|
|
|
2084
2150
|
caches: `
|
|
@@ -2246,6 +2312,223 @@ Input template references: $Input{field} → maps declared input to the probe's
|
|
|
2246
2312
|
Provider status: available | unavailable
|
|
2247
2313
|
`.trim(),
|
|
2248
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
|
+
|
|
2249
2532
|
features: `
|
|
2250
2533
|
DUCTAPE FEATURES
|
|
2251
2534
|
|
|
@@ -2933,9 +3216,15 @@ SESSIONS SERVICE
|
|
|
2933
3216
|
const { token } = await ductape.sessions.refresh({ refreshToken: '...' });
|
|
2934
3217
|
|
|
2935
3218
|
ANALYTICS SERVICE
|
|
2936
|
-
ductape.analytics.
|
|
2937
|
-
ductape.analytics.
|
|
2938
|
-
ductape.analytics.
|
|
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.
|
|
2939
3228
|
|
|
2940
3229
|
FRAMEWORK-SPECIFIC PACKAGES
|
|
2941
3230
|
For React and Vue projects, use the dedicated packages instead of managing the client manually:
|
|
@@ -3037,13 +3326,15 @@ AGENT HOOKS
|
|
|
3037
3326
|
useAgentSignal(hookOptions?) → { mutate, isLoading, error }
|
|
3038
3327
|
|
|
3039
3328
|
BROKER HOOKS
|
|
3040
|
-
useBroker(
|
|
3329
|
+
useBroker()
|
|
3041
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.
|
|
3042
3333
|
useBrokerPublish(hookOptions?)
|
|
3043
3334
|
→ { mutate({ topic, message, headers?, key? }), isLoading, error, data }
|
|
3044
3335
|
useBrokerSubscription(subscribeOptions, hookOptions?)
|
|
3045
3336
|
→ { data: BrokerMessage[], isSubscribed, error, unsubscribe, resubscribe }
|
|
3046
|
-
subscribeOptions: { topic, group? }
|
|
3337
|
+
subscribeOptions: { broker?, topic, group?, session?, product?, env? }
|
|
3047
3338
|
|
|
3048
3339
|
GRAPH HOOKS
|
|
3049
3340
|
useGraph(graphTag) → { isConnected, isConnecting, error, connect, disconnect }
|
|
@@ -3113,7 +3404,10 @@ ACTIONS HOOKS
|
|
|
3113
3404
|
useActionRun(hookOptions?) → { mutate, isLoading, error, data }
|
|
3114
3405
|
|
|
3115
3406
|
ANALYTICS HOOK
|
|
3116
|
-
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" }).
|
|
3117
3411
|
|
|
3118
3412
|
hookOptions pattern (applies to all hooks):
|
|
3119
3413
|
enabled? boolean — false skips auto-fetch/subscribe
|
|
@@ -3283,7 +3577,10 @@ ACTIONS COMPOSABLES
|
|
|
3283
3577
|
useActionRun(composableOptions?) → { mutate, isLoading, error, data }
|
|
3284
3578
|
|
|
3285
3579
|
ANALYTICS COMPOSABLE
|
|
3286
|
-
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" }).
|
|
3287
3584
|
|
|
3288
3585
|
composableOptions pattern (applies to all composables):
|
|
3289
3586
|
enabled? boolean — false skips auto-fetch/subscribe
|
|
@@ -3682,7 +3979,7 @@ async function main() {
|
|
|
3682
3979
|
'index strategy, operation types) that should be confirmed with the user first.\n\n' +
|
|
3683
3980
|
'Available topics: transactions, presave, triggers, aggregations, migrations, indexes, performance, actions, ' +
|
|
3684
3981
|
'graphs, storage, cloud, vector, warehouse, secrets, apps, products, sessions, caches, ' +
|
|
3685
|
-
'notifications, resilience, features, events, logs, client, react, vue',
|
|
3982
|
+
'notifications, resilience, features, events, logs, frontend, frontend-analytics, client, react, vue',
|
|
3686
3983
|
inputSchema: docsInputSchema,
|
|
3687
3984
|
},
|
|
3688
3985
|
docsHandler,
|