zero-com 1.13.0 → 1.13.2
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/README.md +52 -1
- package/lib/runtime.js +9 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -212,6 +212,56 @@ When `getFullName` is called from the client:
|
|
|
212
212
|
4. `getFirstName` executes directly (no transport) with the same context
|
|
213
213
|
5. Both functions can access `context()` with the same data
|
|
214
214
|
|
|
215
|
+
## Calling server functions outside handle()
|
|
216
|
+
|
|
217
|
+
Server functions can be called from any server-side code, not only through the RPC path. This is useful for server-side-only integrations like authentication callbacks, cron jobs, or webhook handlers.
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
// pages/api/auth/[...nextauth].ts (Next.js example)
|
|
221
|
+
import { getUserByCredentials } from '../../../server/auth/funcs'
|
|
222
|
+
|
|
223
|
+
const user = await getUserByCredentials(email, password)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
When called outside `handle()`, the function executes directly with no context. If the function calls `context()` internally, it throws:
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
Error: context() called outside of a server function
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
When called inside `handle()`, context is propagated automatically to the function and any nested `func()` calls, same as the normal RPC path.
|
|
233
|
+
|
|
234
|
+
## File boundary rule
|
|
235
|
+
|
|
236
|
+
Any file that contains `func()` exports is treated as a **server-only module**. On the client build the plugin replaces the **entire file** with lightweight RPC stubs — only the `func()` exports survive, everything else in that file is discarded.
|
|
237
|
+
|
|
238
|
+
This means you must not mix `func()` declarations with client-side code (state, UI utilities, etc.) in the same file. The pattern is the same as Next.js `"use server"` files: one side of the boundary per file.
|
|
239
|
+
|
|
240
|
+
**Wrong** — `connect` will be `undefined` on the client because the whole file is replaced:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
// store.ts ❌
|
|
244
|
+
import { func } from 'zero-com'
|
|
245
|
+
import { createStore } from './create-store'
|
|
246
|
+
|
|
247
|
+
export const { connect, useStore } = createStore(...) // lost on client
|
|
248
|
+
|
|
249
|
+
export const getPhones = func(async () => { ... })
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Right** — separate the RPC calls from client state:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
// funcs.ts ✅ (replaced with stubs on client)
|
|
256
|
+
import { func } from 'zero-com'
|
|
257
|
+
export const getPhones = func(async () => { ... })
|
|
258
|
+
|
|
259
|
+
// store.ts ✅ (untouched on client)
|
|
260
|
+
import { createStore } from './create-store'
|
|
261
|
+
export const { connect, useStore } = createStore(...)
|
|
262
|
+
export * from './funcs'
|
|
263
|
+
```
|
|
264
|
+
|
|
215
265
|
## Plugin options
|
|
216
266
|
|
|
217
267
|
| Option | Type | Description |
|
|
@@ -301,7 +351,8 @@ export const getPhones = func(async (name: string) => {
|
|
|
301
351
|
// server.ts
|
|
302
352
|
import express from 'express';
|
|
303
353
|
import { handle } from 'zero-com';
|
|
304
|
-
|
|
354
|
+
// No manual imports needed — the plugin automatically registers all func() files
|
|
355
|
+
// when it transforms any file that calls handle()
|
|
305
356
|
|
|
306
357
|
const app = express();
|
|
307
358
|
app.use(express.json());
|
package/lib/runtime.js
CHANGED
|
@@ -36,7 +36,11 @@ function context() {
|
|
|
36
36
|
return ctx;
|
|
37
37
|
}
|
|
38
38
|
// Default server-side implementation: call directly from registry
|
|
39
|
-
// This enables server functions to call other server functions without transport
|
|
39
|
+
// This enables server functions to call other server functions without transport.
|
|
40
|
+
// If a request context exists (set by handle()), it is propagated automatically.
|
|
41
|
+
// If there is no context (e.g. called from NextAuth or other server-only code that
|
|
42
|
+
// does not go through handle()), the function is called directly — context() will
|
|
43
|
+
// throw inside the function only if the function actually tries to use it.
|
|
40
44
|
if (typeof globalThis.ZERO_COM_CLIENT_CALL === 'undefined') {
|
|
41
45
|
globalThis.ZERO_COM_CLIENT_CALL = (funcId, args) => {
|
|
42
46
|
var _a;
|
|
@@ -44,13 +48,13 @@ if (typeof globalThis.ZERO_COM_CLIENT_CALL === 'undefined') {
|
|
|
44
48
|
if (!storage) {
|
|
45
49
|
throw new Error('Server function called on client without transport configured. Call call() first.');
|
|
46
50
|
}
|
|
47
|
-
const ctx = storage.getStore();
|
|
48
|
-
if (ctx === undefined) {
|
|
49
|
-
throw new Error('Server function called outside of request context');
|
|
50
|
-
}
|
|
51
51
|
const fn = (_a = globalThis.ZERO_COM_SERVER_REGISTRY) === null || _a === void 0 ? void 0 : _a[funcId];
|
|
52
52
|
if (!fn)
|
|
53
53
|
throw new Error(`Function not found: ${funcId}`);
|
|
54
|
+
const ctx = storage.getStore();
|
|
55
|
+
if (ctx !== undefined) {
|
|
56
|
+
return storage.run(ctx, () => fn(...args));
|
|
57
|
+
}
|
|
54
58
|
return fn(...args);
|
|
55
59
|
};
|
|
56
60
|
}
|