@tthr/vue 0.0.88 → 0.0.90

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/nuxt/module.js CHANGED
@@ -60,6 +60,7 @@ export default defineNuxtModule({
60
60
  nuxt.options.runtimeConfig.public.tether = {
61
61
  projectId,
62
62
  wsUrl,
63
+ auth: options.auth || null,
63
64
  };
64
65
  // Add server API routes for proxying Tether requests
65
66
  // Use .js — Nitro's rollup can't parse .ts from node_modules
package/nuxt/module.ts CHANGED
@@ -21,6 +21,13 @@
21
21
 
22
22
  import { defineNuxtModule, addPlugin, createResolver, addImports, addComponent, addServerHandler, addServerImports } from '@nuxt/kit';
23
23
 
24
+ export interface TetherAuthOptions {
25
+ /** Where to read the auth token from */
26
+ mode: 'localstorage' | 'cookie';
27
+ /** The localStorage key or cookie name to read the token from */
28
+ key: string;
29
+ }
30
+
24
31
  export interface TetherModuleOptions {
25
32
  /** Project ID from Tether dashboard */
26
33
  projectId?: string;
@@ -28,6 +35,8 @@ export interface TetherModuleOptions {
28
35
  url?: string;
29
36
  /** Enable verbose logging for debugging WebSocket connections */
30
37
  verbose?: boolean;
38
+ /** Auth token configuration — attaches the token to all tether requests */
39
+ auth?: TetherAuthOptions;
31
40
  }
32
41
 
33
42
  export default defineNuxtModule<TetherModuleOptions>({
@@ -75,6 +84,7 @@ export default defineNuxtModule<TetherModuleOptions>({
75
84
  (nuxt.options.runtimeConfig.public as any).tether = {
76
85
  projectId,
77
86
  wsUrl,
87
+ auth: options.auth || null,
78
88
  };
79
89
 
80
90
  // Add server API routes for proxying Tether requests
@@ -25,6 +25,50 @@ function toPlainArgs<T>(value: T): T {
25
25
  return out as T;
26
26
  }
27
27
 
28
+ /**
29
+ * Read auth token from the configured source and return as headers.
30
+ * Returns empty object if no auth is configured or no token is found.
31
+ *
32
+ * For localStorage mode, the key supports dot-notation to read nested
33
+ * values from JSON objects, e.g. 'strands_oauth_session.accessToken'
34
+ * reads localStorage key 'strands_oauth_session', parses it as JSON,
35
+ * then accesses the 'accessToken' property.
36
+ */
37
+ function getAuthHeaders(): Record<string, string> {
38
+ if (typeof window === 'undefined') return {};
39
+ const config = (window as any).__TETHER_CONFIG__;
40
+ if (!config?.auth) return {};
41
+
42
+ const { mode, key } = config.auth;
43
+ let token: string | null = null;
44
+
45
+ if (mode === 'localstorage') {
46
+ const parts = key.split('.');
47
+ const storageKey = parts[0];
48
+ const raw = localStorage.getItem(storageKey);
49
+ if (raw && parts.length > 1) {
50
+ try {
51
+ let value: unknown = JSON.parse(raw);
52
+ for (let i = 1; i < parts.length; i++) {
53
+ value = (value as Record<string, unknown>)?.[parts[i]];
54
+ }
55
+ token = typeof value === 'string' ? value : null;
56
+ } catch {
57
+ token = null;
58
+ }
59
+ } else {
60
+ token = raw;
61
+ }
62
+ } else if (mode === 'cookie') {
63
+ const cookieKey = key.split('.')[0];
64
+ const match = document.cookie.match(new RegExp(`(?:^|;\\s*)${cookieKey}=([^;]*)`));
65
+ token = match ? decodeURIComponent(match[1]) : null;
66
+ }
67
+
68
+ if (!token) return {};
69
+ return { Authorization: `Bearer ${token}` };
70
+ }
71
+
28
72
  // ============================================================================
29
73
  // Shared WebSocket Connection Manager
30
74
  // ============================================================================
@@ -309,6 +353,7 @@ export function useQuery<TArgs, TResult>(
309
353
  async () => {
310
354
  const response = await $fetch<{ data: TResult }>('/api/_tether/query', {
311
355
  method: 'POST',
356
+ headers: getAuthHeaders(),
312
357
  body: {
313
358
  function: queryName,
314
359
  args: plainArgs,
@@ -429,6 +474,7 @@ export async function $query<TArgs, TResult>(
429
474
 
430
475
  const response = await $fetch<{ data: TResult }>('/api/_tether/query', {
431
476
  method: 'POST',
477
+ headers: getAuthHeaders(),
432
478
  body: {
433
479
  function: queryName,
434
480
  args: plainArgs,
@@ -492,6 +538,7 @@ export function useMutation<TArgs, TResult>(
492
538
 
493
539
  const response = await $fetch<{ data: TResult }>('/api/_tether/mutation', {
494
540
  method: 'POST',
541
+ headers: getAuthHeaders(),
495
542
  body: {
496
543
  function: mutationName,
497
544
  args: toPlainArgs(args),
@@ -555,6 +602,7 @@ export async function $mutation<TArgs, TResult>(
555
602
 
556
603
  const response = await $fetch<{ data: TResult }>('/api/_tether/mutation', {
557
604
  method: 'POST',
605
+ headers: getAuthHeaders(),
558
606
  body: {
559
607
  function: mutationName,
560
608
  args: plainArgs,
@@ -12,6 +12,10 @@ declare global {
12
12
  __TETHER_CONFIG__?: {
13
13
  projectId: string;
14
14
  wsUrl: string;
15
+ auth?: {
16
+ mode: 'localstorage' | 'cookie';
17
+ key: string;
18
+ } | null;
15
19
  };
16
20
  }
17
21
  }
@@ -19,11 +23,12 @@ declare global {
19
23
  export default defineNuxtPlugin(() => {
20
24
  const config = useRuntimeConfig();
21
25
 
22
- // Make config available for WebSocket connections
23
- // This is safe - no secrets are exposed
26
+ // Make config available for WebSocket connections and auth
27
+ // This is safe - no secrets are exposed (just mode + key name)
24
28
  const tetherConfig = {
25
29
  projectId: config.public.tether?.projectId || '',
26
30
  wsUrl: config.public.tether?.wsUrl || '',
31
+ auth: config.public.tether?.auth || null,
27
32
  };
28
33
 
29
34
  window.__TETHER_CONFIG__ = tetherConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tthr/vue",
3
- "version": "0.0.88",
3
+ "version": "0.0.90",
4
4
  "description": "Tether Vue/Nuxt SDK",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",