@tthr/vue 0.0.89 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -139,7 +139,7 @@ export declare function $query<TArgs, TResult>(query: QueryFunction<TArgs, TResu
139
139
  * ```
140
140
  */
141
141
  export declare function $mutation<TArgs, TResult>(mutation: MutationFunction<TArgs, TResult>, args?: TArgs, options?: UseMutationOptions): Promise<TResult>;
142
- export type { TetherClientOptions, MutationOptions } from '@tthr/client';
142
+ export type { TetherClientOptions, AuthTokenSource, MutationOptions } from '@tthr/client';
143
143
  /**
144
144
  * Tether Nuxt/Vue project configuration
145
145
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/nuxt/module.js CHANGED
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Automatically configures Tether for Nuxt applications.
5
5
  * All API calls are proxied through Nuxt server routes to keep API keys secure.
6
+ * Function execution happens on Tether's Deno runtime — the Nuxt server is a thin proxy.
6
7
  *
7
8
  * Usage in nuxt.config.ts:
8
9
  * ```ts
@@ -114,36 +115,15 @@ export default defineNuxtModule({
114
115
  name: 'useTetherServer',
115
116
  from: resolver.resolve('./runtime/server/utils/tether.js'),
116
117
  },
117
- {
118
- name: 'registerCronHandler',
119
- from: resolver.resolve('./runtime/server/plugins/cron.js'),
120
- },
121
- {
122
- name: 'unregisterCronHandler',
123
- from: resolver.resolve('./runtime/server/plugins/cron.js'),
124
- },
125
- {
126
- name: 'getCronHandlers',
127
- from: resolver.resolve('./runtime/server/plugins/cron.js'),
128
- },
129
118
  ]);
130
119
  // Ensure the runtime files are transpiled
131
120
  nuxt.options.build.transpile = nuxt.options.build.transpile || [];
132
121
  nuxt.options.build.transpile.push('@tthr/vue');
133
- // Add Nitro plugin for cron WebSocket connection
134
- // This auto-connects to Tether when the server starts
122
+ // Ensure Nitro inlines @tthr/vue from node_modules
135
123
  nuxt.hook('nitro:config', (nitroConfig) => {
136
- nitroConfig.plugins = nitroConfig.plugins || [];
137
- nitroConfig.plugins.push(resolver.resolve('./runtime/server/plugins/cron.js'));
138
- // Ensure Nitro inlines and transpiles the plugin from node_modules
139
124
  nitroConfig.externals = nitroConfig.externals || {};
140
125
  nitroConfig.externals.inline = nitroConfig.externals.inline || [];
141
126
  nitroConfig.externals.inline.push('@tthr/vue');
142
- // Also inline the user's tether/functions directory so Nitro transpiles TypeScript
143
- nitroConfig.externals.inline.push(`${nuxt.options.rootDir}/tether`);
144
- // Externalize 'ws' so it's not bundled - needed for WebSocket in Node.js
145
- nitroConfig.externals.external = nitroConfig.externals.external || [];
146
- nitroConfig.externals.external.push('ws');
147
127
  });
148
128
  },
149
129
  });
package/nuxt/module.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Automatically configures Tether for Nuxt applications.
5
5
  * All API calls are proxied through Nuxt server routes to keep API keys secure.
6
+ * Function execution happens on Tether's Deno runtime — the Nuxt server is a thin proxy.
6
7
  *
7
8
  * Usage in nuxt.config.ts:
8
9
  * ```ts
@@ -144,41 +145,17 @@ export default defineNuxtModule<TetherModuleOptions>({
144
145
  name: 'useTetherServer',
145
146
  from: resolver.resolve('./runtime/server/utils/tether.js'),
146
147
  },
147
- {
148
- name: 'registerCronHandler',
149
- from: resolver.resolve('./runtime/server/plugins/cron.js'),
150
- },
151
- {
152
- name: 'unregisterCronHandler',
153
- from: resolver.resolve('./runtime/server/plugins/cron.js'),
154
- },
155
- {
156
- name: 'getCronHandlers',
157
- from: resolver.resolve('./runtime/server/plugins/cron.js'),
158
- },
159
148
  ]);
160
149
 
161
150
  // Ensure the runtime files are transpiled
162
151
  nuxt.options.build.transpile = nuxt.options.build.transpile || [];
163
152
  nuxt.options.build.transpile.push('@tthr/vue');
164
153
 
165
- // Add Nitro plugin for cron WebSocket connection
166
- // This auto-connects to Tether when the server starts
154
+ // Ensure Nitro inlines @tthr/vue from node_modules
167
155
  nuxt.hook('nitro:config' as any, (nitroConfig: any) => {
168
- nitroConfig.plugins = nitroConfig.plugins || [];
169
- nitroConfig.plugins.push(resolver.resolve('./runtime/server/plugins/cron.js'));
170
-
171
- // Ensure Nitro inlines and transpiles the plugin from node_modules
172
156
  nitroConfig.externals = nitroConfig.externals || {};
173
157
  nitroConfig.externals.inline = nitroConfig.externals.inline || [];
174
158
  nitroConfig.externals.inline.push('@tthr/vue');
175
-
176
- // Also inline the user's tether/functions directory so Nitro transpiles TypeScript
177
- nitroConfig.externals.inline.push(`${nuxt.options.rootDir}/tether`);
178
-
179
- // Externalize 'ws' so it's not bundled - needed for WebSocket in Node.js
180
- nitroConfig.externals.external = nitroConfig.externals.external || [];
181
- nitroConfig.externals.external.push('ws');
182
159
  });
183
160
  },
184
161
  });
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Server-side mutation handler
3
- * Executes local custom functions or proxies generic CRUD to Tether API
3
+ * Proxies all requests to Tether API functions execute on Tether's Deno runtime
4
4
  */
5
- import { defineEventHandler, readBody, createError, setResponseStatus } from 'h3';
6
- import { getConfig, lookupFunction, createDatabaseProxy, buildAuthContext, proxyToTetherApi, log, } from './utils/handler.js';
5
+ import { defineEventHandler, readBody, createError } from 'h3';
6
+ import { getConfig, proxyToTetherApi, getAuthHeaders, log } from './utils/handler.js';
7
7
  export default defineEventHandler(async (event) => {
8
8
  const config = getConfig();
9
9
  const body = await readBody(event);
@@ -13,27 +13,7 @@ export default defineEventHandler(async (event) => {
13
13
  message: 'Missing "function" in request body',
14
14
  });
15
15
  }
16
- const customFn = await lookupFunction(body.function);
17
- log.debug(`Mutation: ${body.function}, custom function found: ${!!customFn}`);
18
- if (customFn) {
19
- try {
20
- log.debug(`Executing custom mutation: ${body.function}`);
21
- const db = createDatabaseProxy(config.apiKey, config.url, config.projectId);
22
- const { auth, ctx } = await buildAuthContext(event, config.url, config.projectId);
23
- const result = await customFn.handler({ db, ctx, auth, args: body.args ?? {} });
24
- return { data: result };
25
- }
26
- catch (error) {
27
- const err = error instanceof Error ? error : new Error(String(error));
28
- log.error(`Mutation ${body.function} failed:`, err);
29
- // Return JSON error response instead of throwing (avoids proxy HTML error pages)
30
- setResponseStatus(event, 400);
31
- return {
32
- error: true,
33
- message: err.message || 'Mutation execution failed',
34
- function: body.function,
35
- };
36
- }
37
- }
38
- return proxyToTetherApi(config, 'mutation', body.function, body.args);
16
+ log.debug(`Mutation: ${body.function}`);
17
+ const authHeaders = getAuthHeaders(event);
18
+ return proxyToTetherApi(config, 'mutation', body.function, body.args, authHeaders);
39
19
  });
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Server-side query handler
3
- * Executes local custom functions or proxies generic CRUD to Tether API
3
+ * Proxies all requests to Tether API functions execute on Tether's Deno runtime
4
4
  */
5
5
  import { defineEventHandler, readBody, createError } from 'h3';
6
- import { getConfig, lookupFunction, createDatabaseProxy, buildAuthContext, proxyToTetherApi, log, } from './utils/handler.js';
6
+ import { getConfig, proxyToTetherApi, getAuthHeaders, log } from './utils/handler.js';
7
7
  export default defineEventHandler(async (event) => {
8
8
  const config = getConfig();
9
9
  const body = await readBody(event);
@@ -13,25 +13,7 @@ export default defineEventHandler(async (event) => {
13
13
  message: 'Missing "function" in request body',
14
14
  });
15
15
  }
16
- const customFn = await lookupFunction(body.function);
17
- log.debug(`Query: ${body.function}, custom function found: ${!!customFn}`);
18
- if (customFn) {
19
- try {
20
- const db = createDatabaseProxy(config.apiKey, config.url, config.projectId);
21
- const { auth, ctx } = await buildAuthContext(event, config.url, config.projectId);
22
- log.debug(`Executing custom function: ${body.function}`);
23
- const result = await customFn.handler({ db, ctx, auth, args: body.args ?? {} });
24
- log.debug(`Function ${body.function} completed`);
25
- return { data: result };
26
- }
27
- catch (error) {
28
- const err = error instanceof Error ? error : new Error(String(error));
29
- log.error(`Function ${body.function} failed:`, err.message);
30
- throw createError({
31
- statusCode: 500,
32
- message: err.message || 'Function execution failed',
33
- });
34
- }
35
- }
36
- return proxyToTetherApi(config, 'query', body.function, body.args);
16
+ log.debug(`Query: ${body.function}`);
17
+ const authHeaders = getAuthHeaders(event);
18
+ return proxyToTetherApi(config, 'query', body.function, body.args, authHeaders);
37
19
  });
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Shared handler utilities for Tether server routes
3
3
  *
4
- * Provides function registry loading, database proxy creation,
5
- * auth context building, and logging used by both query and mutation handlers.
4
+ * Provides config loading, auth header extraction, and API proxying.
5
+ * All function execution happens on Tether's Deno runtime the Nuxt
6
+ * server only proxies requests to keep the API key server-side.
6
7
  */
7
8
  import { createError, getHeader, getCookie } from 'h3';
8
9
  import { useRuntimeConfig } from '#imports';
@@ -42,265 +43,47 @@ export function getConfig() {
42
43
  }
43
44
  return { apiKey, url, projectId };
44
45
  }
45
- let functionRegistry = null;
46
- let registryError = null;
47
- /**
48
- * Load the user's custom functions from ~/tether/functions (once).
49
- */
50
- async function loadFunctionRegistry() {
51
- if (functionRegistry !== null || registryError !== null)
52
- return;
53
- try {
54
- log.debug('Loading custom functions from ~~/tether/functions/index.ts');
55
- const functions = await import('~~/tether/functions/index.ts').catch((err) => {
56
- log.debug('Failed to import functions:', err.message);
57
- return null;
58
- });
59
- if (functions) {
60
- log.debug('Loaded function modules:', Object.keys(functions));
61
- functionRegistry = functions;
62
- }
63
- else {
64
- log.debug('No custom functions found, will proxy all requests');
65
- functionRegistry = {};
66
- }
67
- }
68
- catch (error) {
69
- const err = error instanceof Error ? error : new Error(String(error));
70
- log.debug('Could not load custom functions:', err.message);
71
- registryError = err;
72
- functionRegistry = {};
73
- }
74
- }
75
- /**
76
- * Look up a function by name (e.g. "channel.getMyChannels").
77
- * Returns null if no custom function is registered.
78
- */
79
- export async function lookupFunction(name) {
80
- await loadFunctionRegistry();
81
- if (!functionRegistry || !name)
82
- return null;
83
- const parts = name.split('.');
84
- if (parts.length !== 2)
85
- return null;
86
- const [moduleName, fnName] = parts;
87
- const mod = functionRegistry[moduleName];
88
- if (!mod)
89
- return null;
90
- const fn = mod[fnName];
91
- if (fn && typeof fn === 'object' && typeof fn.handler === 'function') {
92
- return fn;
93
- }
94
- return null;
95
- }
96
46
  // ============================================================================
97
- // Database Proxy
47
+ // Auth Header Extraction
98
48
  // ============================================================================
99
49
  /**
100
- * Create a database proxy that routes calls to Tether's CRUD endpoints.
50
+ * Extract auth-related headers from the incoming request to forward to Tether API.
51
+ * This allows the Deno runtime to identify the end user.
101
52
  */
102
- export function createDatabaseProxy(apiKey, url, projectId) {
103
- return new Proxy({}, {
104
- get(_target, tableName) {
105
- const makeRequest = async (operation, args) => {
106
- const response = await fetch(`${url}/api/v1/projects/${projectId}/query`, {
107
- method: 'POST',
108
- headers: {
109
- 'Content-Type': 'application/json',
110
- 'Authorization': `Bearer ${apiKey}`,
111
- },
112
- body: JSON.stringify({
113
- function: `${tableName}.${operation}`,
114
- args,
115
- }),
116
- });
117
- if (!response.ok) {
118
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
119
- throw new Error(error.error || `Database operation failed: ${tableName}.${operation}`);
120
- }
121
- const result = await response.json();
122
- return result.data;
123
- };
124
- const makeMutation = async (operation, args) => {
125
- const response = await fetch(`${url}/api/v1/projects/${projectId}/mutation`, {
126
- method: 'POST',
127
- headers: {
128
- 'Content-Type': 'application/json',
129
- 'Authorization': `Bearer ${apiKey}`,
130
- },
131
- body: JSON.stringify({
132
- function: `${tableName}.${operation}`,
133
- args,
134
- }),
135
- });
136
- if (!response.ok) {
137
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
138
- throw new Error(error.error || `Database operation failed: ${tableName}.${operation}`);
139
- }
140
- const result = await response.json();
141
- return result.data;
142
- };
143
- return {
144
- findMany: async (options) => {
145
- const args = {};
146
- if (options?.where)
147
- args.where = options.where;
148
- if (options?.limit)
149
- args.limit = options.limit;
150
- if (options?.offset)
151
- args.offset = options.offset;
152
- if (options?.orderBy) {
153
- if (typeof options.orderBy === 'string') {
154
- args.orderBy = options.orderBy;
155
- }
156
- else if (typeof options.orderBy === 'object') {
157
- const [column, dir] = Object.entries(options.orderBy)[0] || [];
158
- if (column) {
159
- args.orderBy = column;
160
- args.orderDir = dir?.toUpperCase?.() || 'ASC';
161
- }
162
- }
163
- }
164
- if (options?.orderDir)
165
- args.orderDir = options.orderDir;
166
- return makeRequest('list', args);
167
- },
168
- findFirst: async (options) => {
169
- const args = { limit: 1 };
170
- if (options?.where)
171
- args.where = options.where;
172
- const results = await makeRequest('list', args);
173
- return results?.[0] ?? null;
174
- },
175
- findUnique: async (options) => {
176
- const args = { limit: 1 };
177
- if (options?.where)
178
- args.where = options.where;
179
- const results = await makeRequest('list', args);
180
- return results?.[0] ?? null;
181
- },
182
- findById: async (id) => {
183
- return makeRequest('get', { id });
184
- },
185
- count: async (options) => {
186
- const args = {};
187
- if (options?.where)
188
- args.where = options.where;
189
- const result = await makeRequest('count', args);
190
- return result?.count ?? 0;
191
- },
192
- insert: async (data) => {
193
- return makeMutation('create', { data });
194
- },
195
- insertMany: async (items) => {
196
- const results = [];
197
- for (const data of items) {
198
- const result = await makeMutation('create', { data });
199
- results.push(result);
200
- }
201
- return results;
202
- },
203
- create: async (options) => {
204
- return makeMutation('create', { data: options.data });
205
- },
206
- update: async (options) => {
207
- const id = options.where?._id ?? options.where?.id;
208
- if (!id) {
209
- throw new Error('Update requires an _id in the where clause');
210
- }
211
- const result = await makeMutation('update', { id, data: options.data });
212
- return result?.rowsAffected ?? 0;
213
- },
214
- upsert: async (options) => {
215
- const results = await makeRequest('list', { where: options.where, limit: 1 }).catch(() => []);
216
- const existing = results?.[0] ?? null;
217
- if (existing) {
218
- const id = existing._id ?? existing.id;
219
- if (!id) {
220
- throw new Error('Found record has no _id or id field for update');
221
- }
222
- await makeMutation('update', { id, data: options.update });
223
- return { ...existing, ...options.update };
224
- }
225
- else {
226
- return makeMutation('create', { data: options.create });
227
- }
228
- },
229
- delete: async (options) => {
230
- const id = options.where?._id ?? options.where?.id;
231
- if (id) {
232
- const result = await makeMutation('delete', { id });
233
- return result?.rowsAffected ?? 0;
234
- }
235
- const result = await makeMutation('deleteMany', { where: options.where });
236
- return result?.rowsAffected ?? 0;
237
- },
238
- deleteById: async (id) => {
239
- const result = await makeMutation('delete', { id });
240
- return (result?.rowsAffected ?? 0) > 0;
241
- },
242
- };
243
- },
244
- });
245
- }
246
- /**
247
- * Build auth context by extracting and validating tokens from the request.
248
- */
249
- export async function buildAuthContext(event, url, projectId) {
250
- let userIdentity = null;
251
- const authHeader = getHeader(event, 'authorization');
53
+ export function getAuthHeaders(event) {
54
+ const headers = {};
55
+ // Forward Strands auth token
252
56
  const strandsToken = getHeader(event, 'x-strands-token');
57
+ if (strandsToken) {
58
+ headers['x-auth-token'] = strandsToken;
59
+ }
60
+ // Forward cookie-based auth token
253
61
  const cookieToken = getCookie(event, 'strands_oauth_token');
254
- if (strandsToken || cookieToken || authHeader?.startsWith('Bearer ')) {
255
- const token = strandsToken || cookieToken || authHeader?.replace('Bearer ', '');
256
- if (token) {
257
- try {
258
- const authResponse = await fetch(`${url}/api/v1/projects/${projectId}/auth/validate`, {
259
- method: 'POST',
260
- headers: { 'Content-Type': 'application/json' },
261
- body: JSON.stringify({ accessToken: token }),
262
- });
263
- if (authResponse.ok) {
264
- const authData = await authResponse.json();
265
- if (authData.valid) {
266
- userIdentity = {
267
- subject: authData.userId,
268
- email: authData.email,
269
- name: authData.name,
270
- };
271
- }
272
- }
273
- }
274
- catch (authError) {
275
- const err = authError instanceof Error ? authError : new Error(String(authError));
276
- log.warn('Auth validation failed:', err.message);
277
- }
278
- }
62
+ if (cookieToken) {
63
+ headers['x-auth-token'] = cookieToken;
279
64
  }
280
- const auth = {
281
- getUserIdentity: async () => userIdentity,
282
- };
283
- const ctx = {
284
- auth: {
285
- userId: userIdentity?.subject ?? null,
286
- claims: userIdentity ?? {},
287
- },
288
- userId: userIdentity?.subject ?? null,
289
- };
290
- return { auth, ctx };
65
+ // Forward Bearer token from Authorization header (end-user token, not API key)
66
+ const authHeader = getHeader(event, 'authorization');
67
+ if (authHeader?.startsWith('Bearer ')) {
68
+ const token = authHeader.replace('Bearer ', '');
69
+ headers['x-auth-token'] = token;
70
+ }
71
+ return headers;
291
72
  }
292
73
  // ============================================================================
293
74
  // Proxy to Tether API
294
75
  // ============================================================================
295
76
  /**
296
- * Proxy a request directly to the Tether API (for generic CRUD when no custom function exists).
77
+ * Proxy a request to the Tether API.
78
+ * Uses the API key for authentication and forwards any end-user auth headers.
297
79
  */
298
- export async function proxyToTetherApi(config, endpoint, functionName, args) {
80
+ export async function proxyToTetherApi(config, endpoint, functionName, args, extraHeaders) {
299
81
  const response = await fetch(`${config.url}/api/v1/projects/${config.projectId}/${endpoint}`, {
300
82
  method: 'POST',
301
83
  headers: {
302
84
  'Content-Type': 'application/json',
303
85
  'Authorization': `Bearer ${config.apiKey}`,
86
+ ...extraHeaders,
304
87
  },
305
88
  body: JSON.stringify({
306
89
  function: functionName,
@@ -1,17 +1,10 @@
1
1
  /**
2
2
  * Server-side mutation handler
3
- * Executes local custom functions or proxies generic CRUD to Tether API
3
+ * Proxies all requests to Tether API functions execute on Tether's Deno runtime
4
4
  */
5
5
 
6
- import { defineEventHandler, readBody, createError, setResponseStatus } from 'h3';
7
- import {
8
- getConfig,
9
- lookupFunction,
10
- createDatabaseProxy,
11
- buildAuthContext,
12
- proxyToTetherApi,
13
- log,
14
- } from './utils/handler.js';
6
+ import { defineEventHandler, readBody, createError } from 'h3';
7
+ import { getConfig, proxyToTetherApi, getAuthHeaders, log } from './utils/handler.js';
15
8
 
16
9
  export default defineEventHandler(async (event) => {
17
10
  const config = getConfig();
@@ -24,30 +17,8 @@ export default defineEventHandler(async (event) => {
24
17
  });
25
18
  }
26
19
 
27
- const customFn = await lookupFunction(body.function);
28
- log.debug(`Mutation: ${body.function}, custom function found: ${!!customFn}`);
20
+ log.debug(`Mutation: ${body.function}`);
29
21
 
30
- if (customFn) {
31
- try {
32
- log.debug(`Executing custom mutation: ${body.function}`);
33
- const db = createDatabaseProxy(config.apiKey, config.url, config.projectId);
34
- const { auth, ctx } = await buildAuthContext(event, config.url, config.projectId);
35
-
36
- const result = await customFn.handler({ db, ctx, auth, args: body.args ?? {} });
37
-
38
- return { data: result };
39
- } catch (error: unknown) {
40
- const err = error instanceof Error ? error : new Error(String(error));
41
- log.error(`Mutation ${body.function} failed:`, err);
42
- // Return JSON error response instead of throwing (avoids proxy HTML error pages)
43
- setResponseStatus(event, 400);
44
- return {
45
- error: true,
46
- message: err.message || 'Mutation execution failed',
47
- function: body.function,
48
- };
49
- }
50
- }
51
-
52
- return proxyToTetherApi(config, 'mutation', body.function, body.args);
22
+ const authHeaders = getAuthHeaders(event);
23
+ return proxyToTetherApi(config, 'mutation', body.function, body.args, authHeaders);
53
24
  });
@@ -1,17 +1,10 @@
1
1
  /**
2
2
  * Server-side query handler
3
- * Executes local custom functions or proxies generic CRUD to Tether API
3
+ * Proxies all requests to Tether API functions execute on Tether's Deno runtime
4
4
  */
5
5
 
6
6
  import { defineEventHandler, readBody, createError } from 'h3';
7
- import {
8
- getConfig,
9
- lookupFunction,
10
- createDatabaseProxy,
11
- buildAuthContext,
12
- proxyToTetherApi,
13
- log,
14
- } from './utils/handler.js';
7
+ import { getConfig, proxyToTetherApi, getAuthHeaders, log } from './utils/handler.js';
15
8
 
16
9
  export default defineEventHandler(async (event) => {
17
10
  const config = getConfig();
@@ -24,28 +17,8 @@ export default defineEventHandler(async (event) => {
24
17
  });
25
18
  }
26
19
 
27
- const customFn = await lookupFunction(body.function);
28
- log.debug(`Query: ${body.function}, custom function found: ${!!customFn}`);
20
+ log.debug(`Query: ${body.function}`);
29
21
 
30
- if (customFn) {
31
- try {
32
- const db = createDatabaseProxy(config.apiKey, config.url, config.projectId);
33
- const { auth, ctx } = await buildAuthContext(event, config.url, config.projectId);
34
-
35
- log.debug(`Executing custom function: ${body.function}`);
36
- const result = await customFn.handler({ db, ctx, auth, args: body.args ?? {} });
37
- log.debug(`Function ${body.function} completed`);
38
-
39
- return { data: result };
40
- } catch (error: unknown) {
41
- const err = error instanceof Error ? error : new Error(String(error));
42
- log.error(`Function ${body.function} failed:`, err.message);
43
- throw createError({
44
- statusCode: 500,
45
- message: err.message || 'Function execution failed',
46
- });
47
- }
48
- }
49
-
50
- return proxyToTetherApi(config, 'query', body.function, body.args);
22
+ const authHeaders = getAuthHeaders(event);
23
+ return proxyToTetherApi(config, 'query', body.function, body.args, authHeaders);
51
24
  });
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Shared handler utilities for Tether server routes
3
3
  *
4
- * Provides function registry loading, database proxy creation,
5
- * auth context building, and logging used by both query and mutation handlers.
4
+ * Provides config loading, auth header extraction, and API proxying.
5
+ * All function execution happens on Tether's Deno runtime the Nuxt
6
+ * server only proxies requests to keep the API key server-side.
6
7
  */
7
8
 
8
9
  import { createError, getHeader, getCookie, type H3Event } from 'h3';
@@ -62,280 +63,36 @@ export function getConfig(): TetherServerConfig {
62
63
  }
63
64
 
64
65
  // ============================================================================
65
- // Function Registry
66
+ // Auth Header Extraction
66
67
  // ============================================================================
67
68
 
68
- interface TetherFunction {
69
- handler: (ctx: unknown) => Promise<unknown>;
70
- [key: string]: unknown;
71
- }
72
-
73
- type FunctionRegistry = Record<string, Record<string, TetherFunction>>;
74
-
75
- let functionRegistry: FunctionRegistry | null = null;
76
- let registryError: Error | null = null;
77
-
78
69
  /**
79
- * Load the user's custom functions from ~/tether/functions (once).
70
+ * Extract auth-related headers from the incoming request to forward to Tether API.
71
+ * This allows the Deno runtime to identify the end user.
80
72
  */
81
- async function loadFunctionRegistry(): Promise<void> {
82
- if (functionRegistry !== null || registryError !== null) return;
83
-
84
- try {
85
- log.debug('Loading custom functions from ~~/tether/functions/index.ts');
86
- const functions = await import('~~/tether/functions/index.ts').catch((err: Error) => {
87
- log.debug('Failed to import functions:', err.message);
88
- return null;
89
- });
73
+ export function getAuthHeaders(event: H3Event): Record<string, string> {
74
+ const headers: Record<string, string> = {};
90
75
 
91
- if (functions) {
92
- log.debug('Loaded function modules:', Object.keys(functions));
93
- functionRegistry = functions as FunctionRegistry;
94
- } else {
95
- log.debug('No custom functions found, will proxy all requests');
96
- functionRegistry = {};
97
- }
98
- } catch (error: unknown) {
99
- const err = error instanceof Error ? error : new Error(String(error));
100
- log.debug('Could not load custom functions:', err.message);
101
- registryError = err;
102
- functionRegistry = {};
76
+ // Forward Strands auth token
77
+ const strandsToken = getHeader(event, 'x-strands-token');
78
+ if (strandsToken) {
79
+ headers['x-auth-token'] = strandsToken;
103
80
  }
104
- }
105
-
106
- /**
107
- * Look up a function by name (e.g. "channel.getMyChannels").
108
- * Returns null if no custom function is registered.
109
- */
110
- export async function lookupFunction(name: string): Promise<TetherFunction | null> {
111
- await loadFunctionRegistry();
112
-
113
- if (!functionRegistry || !name) return null;
114
81
 
115
- const parts = name.split('.');
116
- if (parts.length !== 2) return null;
117
-
118
- const [moduleName, fnName] = parts;
119
- const mod = functionRegistry[moduleName];
120
- if (!mod) return null;
121
-
122
- const fn = mod[fnName];
123
- if (fn && typeof fn === 'object' && typeof fn.handler === 'function') {
124
- return fn;
82
+ // Forward cookie-based auth token
83
+ const cookieToken = getCookie(event, 'strands_oauth_token');
84
+ if (cookieToken) {
85
+ headers['x-auth-token'] = cookieToken;
125
86
  }
126
87
 
127
- return null;
128
- }
129
-
130
- // ============================================================================
131
- // Database Proxy
132
- // ============================================================================
133
-
134
- /**
135
- * Create a database proxy that routes calls to Tether's CRUD endpoints.
136
- */
137
- export function createDatabaseProxy(apiKey: string, url: string, projectId: string) {
138
- return new Proxy({} as Record<string, unknown>, {
139
- get(_target, tableName: string) {
140
- const makeRequest = async (operation: string, args: Record<string, unknown>) => {
141
- const response = await fetch(`${url}/api/v1/projects/${projectId}/query`, {
142
- method: 'POST',
143
- headers: {
144
- 'Content-Type': 'application/json',
145
- 'Authorization': `Bearer ${apiKey}`,
146
- },
147
- body: JSON.stringify({
148
- function: `${tableName}.${operation}`,
149
- args,
150
- }),
151
- });
152
-
153
- if (!response.ok) {
154
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
155
- throw new Error(error.error || `Database operation failed: ${tableName}.${operation}`);
156
- }
157
-
158
- const result = await response.json();
159
- return result.data;
160
- };
161
-
162
- const makeMutation = async (operation: string, args: Record<string, unknown>) => {
163
- const response = await fetch(`${url}/api/v1/projects/${projectId}/mutation`, {
164
- method: 'POST',
165
- headers: {
166
- 'Content-Type': 'application/json',
167
- 'Authorization': `Bearer ${apiKey}`,
168
- },
169
- body: JSON.stringify({
170
- function: `${tableName}.${operation}`,
171
- args,
172
- }),
173
- });
174
-
175
- if (!response.ok) {
176
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
177
- throw new Error(error.error || `Database operation failed: ${tableName}.${operation}`);
178
- }
179
-
180
- const result = await response.json();
181
- return result.data;
182
- };
183
-
184
- return {
185
- findMany: async (options?: { where?: Record<string, unknown>; limit?: number; offset?: number; orderBy?: string | Record<string, string>; orderDir?: string }) => {
186
- const args: Record<string, unknown> = {};
187
- if (options?.where) args.where = options.where;
188
- if (options?.limit) args.limit = options.limit;
189
- if (options?.offset) args.offset = options.offset;
190
- if (options?.orderBy) {
191
- if (typeof options.orderBy === 'string') {
192
- args.orderBy = options.orderBy;
193
- } else if (typeof options.orderBy === 'object') {
194
- const [column, dir] = Object.entries(options.orderBy)[0] || [];
195
- if (column) {
196
- args.orderBy = column;
197
- args.orderDir = dir?.toUpperCase?.() || 'ASC';
198
- }
199
- }
200
- }
201
- if (options?.orderDir) args.orderDir = options.orderDir;
202
- return makeRequest('list', args);
203
- },
204
- findFirst: async (options?: { where?: Record<string, unknown> }) => {
205
- const args: Record<string, unknown> = { limit: 1 };
206
- if (options?.where) args.where = options.where;
207
- const results = await makeRequest('list', args);
208
- return results?.[0] ?? null;
209
- },
210
- findUnique: async (options?: { where?: Record<string, unknown> }) => {
211
- const args: Record<string, unknown> = { limit: 1 };
212
- if (options?.where) args.where = options.where;
213
- const results = await makeRequest('list', args);
214
- return results?.[0] ?? null;
215
- },
216
- findById: async (id: unknown) => {
217
- return makeRequest('get', { id });
218
- },
219
- count: async (options?: { where?: Record<string, unknown> }) => {
220
- const args: Record<string, unknown> = {};
221
- if (options?.where) args.where = options.where;
222
- const result = await makeRequest('count', args);
223
- return result?.count ?? 0;
224
- },
225
- insert: async (data: unknown) => {
226
- return makeMutation('create', { data });
227
- },
228
- insertMany: async (items: unknown[]) => {
229
- const results: unknown[] = [];
230
- for (const data of items) {
231
- const result = await makeMutation('create', { data });
232
- results.push(result);
233
- }
234
- return results;
235
- },
236
- create: async (options: { data: unknown }) => {
237
- return makeMutation('create', { data: options.data });
238
- },
239
- update: async (options: { where: Record<string, unknown>; data: unknown }) => {
240
- const id = options.where?._id ?? options.where?.id;
241
- if (!id) {
242
- throw new Error('Update requires an _id in the where clause');
243
- }
244
- const result = await makeMutation('update', { id, data: options.data });
245
- return result?.rowsAffected ?? 0;
246
- },
247
- upsert: async (options: { where: Record<string, unknown>; create: unknown; update: unknown }) => {
248
- const results = await makeRequest('list', { where: options.where, limit: 1 }).catch(() => []);
249
- const existing = results?.[0] ?? null;
250
-
251
- if (existing) {
252
- const id = existing._id ?? existing.id;
253
- if (!id) {
254
- throw new Error('Found record has no _id or id field for update');
255
- }
256
- await makeMutation('update', { id, data: options.update });
257
- return { ...existing, ...options.update };
258
- } else {
259
- return makeMutation('create', { data: options.create });
260
- }
261
- },
262
- delete: async (options: { where: Record<string, unknown> }) => {
263
- const id = options.where?._id ?? options.where?.id;
264
- if (id) {
265
- const result = await makeMutation('delete', { id });
266
- return result?.rowsAffected ?? 0;
267
- }
268
- const result = await makeMutation('deleteMany', { where: options.where });
269
- return result?.rowsAffected ?? 0;
270
- },
271
- deleteById: async (id: unknown) => {
272
- const result = await makeMutation('delete', { id });
273
- return (result?.rowsAffected ?? 0) > 0;
274
- },
275
- };
276
- },
277
- });
278
- }
279
-
280
- // ============================================================================
281
- // Auth Context
282
- // ============================================================================
283
-
284
- interface UserIdentity {
285
- subject: string;
286
- email?: string;
287
- name?: string;
288
- }
289
-
290
- /**
291
- * Build auth context by extracting and validating tokens from the request.
292
- */
293
- export async function buildAuthContext(event: H3Event, url: string, projectId: string) {
294
- let userIdentity: UserIdentity | null = null;
295
-
88
+ // Forward Bearer token from Authorization header (end-user token, not API key)
296
89
  const authHeader = getHeader(event, 'authorization');
297
- const strandsToken = getHeader(event, 'x-strands-token');
298
- const cookieToken = getCookie(event, 'strands_oauth_token');
299
-
300
- if (strandsToken || cookieToken || authHeader?.startsWith('Bearer ')) {
301
- const token = strandsToken || cookieToken || authHeader?.replace('Bearer ', '');
302
- if (token) {
303
- try {
304
- const authResponse = await fetch(`${url}/api/v1/projects/${projectId}/auth/validate`, {
305
- method: 'POST',
306
- headers: { 'Content-Type': 'application/json' },
307
- body: JSON.stringify({ accessToken: token }),
308
- });
309
- if (authResponse.ok) {
310
- const authData = await authResponse.json();
311
- if (authData.valid) {
312
- userIdentity = {
313
- subject: authData.userId,
314
- email: authData.email,
315
- name: authData.name,
316
- };
317
- }
318
- }
319
- } catch (authError: unknown) {
320
- const err = authError instanceof Error ? authError : new Error(String(authError));
321
- log.warn('Auth validation failed:', err.message);
322
- }
323
- }
90
+ if (authHeader?.startsWith('Bearer ')) {
91
+ const token = authHeader.replace('Bearer ', '');
92
+ headers['x-auth-token'] = token;
324
93
  }
325
94
 
326
- const auth = {
327
- getUserIdentity: async () => userIdentity,
328
- };
329
-
330
- const ctx = {
331
- auth: {
332
- userId: userIdentity?.subject ?? null,
333
- claims: userIdentity ?? {},
334
- },
335
- userId: userIdentity?.subject ?? null,
336
- };
337
-
338
- return { auth, ctx };
95
+ return headers;
339
96
  }
340
97
 
341
98
  // ============================================================================
@@ -343,19 +100,22 @@ export async function buildAuthContext(event: H3Event, url: string, projectId: s
343
100
  // ============================================================================
344
101
 
345
102
  /**
346
- * Proxy a request directly to the Tether API (for generic CRUD when no custom function exists).
103
+ * Proxy a request to the Tether API.
104
+ * Uses the API key for authentication and forwards any end-user auth headers.
347
105
  */
348
106
  export async function proxyToTetherApi(
349
107
  config: TetherServerConfig,
350
108
  endpoint: 'query' | 'mutation',
351
109
  functionName: string,
352
- args: unknown
110
+ args: unknown,
111
+ extraHeaders?: Record<string, string>,
353
112
  ) {
354
113
  const response = await fetch(`${config.url}/api/v1/projects/${config.projectId}/${endpoint}`, {
355
114
  method: 'POST',
356
115
  headers: {
357
116
  'Content-Type': 'application/json',
358
117
  'Authorization': `Bearer ${config.apiKey}`,
118
+ ...extraHeaders,
359
119
  },
360
120
  body: JSON.stringify({
361
121
  function: functionName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tthr/vue",
3
- "version": "0.0.89",
3
+ "version": "0.1.0",
4
4
  "description": "Tether Vue/Nuxt SDK",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -17,9 +17,6 @@
17
17
  "./nuxt": {
18
18
  "import": "./nuxt/module.ts"
19
19
  },
20
- "./nuxt/runtime/server/plugins/cron": {
21
- "import": "./nuxt/runtime/server/plugins/cron.js"
22
- },
23
20
  "./nuxt/runtime/server/utils/tether": {
24
21
  "import": "./nuxt/runtime/server/utils/tether.js"
25
22
  }
@@ -37,12 +34,10 @@
37
34
  ],
38
35
  "dependencies": {
39
36
  "@nuxt/kit": "^3.14.0",
40
- "@tthr/client": "latest",
41
- "@tthr/server": "latest",
42
- "ws": "^8.18.0"
37
+ "@tthr/client": "0.1.0",
38
+ "@tthr/server": "0.0.17"
43
39
  },
44
40
  "devDependencies": {
45
- "@types/ws": "^8.5.13",
46
41
  "typescript": "^5.7.2"
47
42
  },
48
43
  "peerDependencies": {